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 defines classes SKGObjectModel.
8 *
9 * @author Stephane MANKOWSKI / Guillaume DE BURE
10  */
11 #include "skgobjectmodel.h"
12 
13 #include <math.h>
14 
15 #include <kconfiggroup.h>
16 #include <klocalizedstring.h>
17 
18 #include <qapplication.h>
19 #include <qcolor.h>
20 #include <qdir.h>
21 #include <qfont.h>
22 #include <qicon.h>
23 #include <qmimedata.h>
24 #include <qstandardpaths.h>
25 
26 #include "skgaccountobject.h"
27 #include "skgbudgetobject.h"
28 #include "skgbudgetruleobject.h"
29 #include "skgcategoryobject.h"
30 #include "skgdocumentbank.h"
31 #include "skgmainpanel.h"
32 #include "skgoperationobject.h"
33 #include "skgpayeeobject.h"
34 #include "skgrecurrentoperationobject.h"
35 #include "skgsuboperationobject.h"
36 #include "skgtraces.h"
37 #include "skgtrackerobject.h"
38 #include "skgtransactionmng.h"
39 #include "skgunitobject.h"
40 #include "skgunitvalueobject.h"
41 
42 SKGObjectModel::
SKGObjectModel(SKGDocumentBank * iDocument,const QString & iTable,const QString & iWhereClause,QWidget * iParent,const QString & iParentAttribute,bool iResetOnCreation)43 SKGObjectModel(SKGDocumentBank* iDocument,
44                const QString& iTable,
45                const QString& iWhereClause,
46                QWidget* iParent,
47                const QString& iParentAttribute,
48                bool iResetOnCreation)
49     : SKGObjectModelBase(iDocument, iTable, iWhereClause, iParent, iParentAttribute, false)
50 {
51     SKGTRACEINFUNC(1)
52 
53     m_operationTable = false;
54     m_recurrentoperationTable = false;
55     m_trackerTable = false;
56     m_accountTable = false;
57     m_unitTable = false;
58     m_unitvalueTable = false;
59     m_suboperationTable = false;
60     m_categoryTable = false;
61     m_ruleTable = false;
62     m_interestTable = false;
63     m_interestResultTable = false;
64     m_payeeTable = false;
65     m_budgetTable = false;
66     m_budgetRuleTable = false;
67     m_isResetRealyNeeded = iResetOnCreation;
68     refresh();
69 }
70 
buidCache()71 void SKGObjectModel::buidCache()
72 {
73     SKGObjectModelBase::buidCache();
74     m_operationTable = (getRealTable() == QStringLiteral("operation") || getRealTable() == QStringLiteral("suboperation"));
75     m_payeeTable = (getRealTable() == QStringLiteral("payee"));
76     m_trackerTable = (getRealTable() == QStringLiteral("refund"));
77     m_recurrentoperationTable = (getRealTable() == QStringLiteral("recurrentoperation"));
78     m_accountTable = (getRealTable() == QStringLiteral("account"));
79     m_unitTable = (getRealTable() == QStringLiteral("unit"));
80     m_unitvalueTable = (getRealTable() == QStringLiteral("unitvalue"));
81     m_suboperationTable = (getTable() == QStringLiteral("v_suboperation_consolidated"));
82     m_ruleTable = (getRealTable() == QStringLiteral("rule"));
83     m_categoryTable = (getRealTable() == QStringLiteral("category"));
84     m_interestTable = (getRealTable() == QStringLiteral("interest"));
85     m_interestResultTable = (getRealTable() == QStringLiteral("interest_result"));
86     m_budgetTable = (getRealTable() == QStringLiteral("budget"));
87     m_budgetRuleTable = (getRealTable() == QStringLiteral("budgetrule"));
88 
89     if (m_unitvalueTable) {
90         SKGUnitValueObject unitValObject(getObject(this->index(0, 0)));
91         SKGUnitObject unitObject;
92         unitValObject.getUnit(unitObject);
93         SKGUnitObject parentUnit;
94         unitObject.getUnit(parentUnit);
95         if (parentUnit.exist()) {
96             m_cacheUnit.Name = parentUnit.getName();
97             m_cacheUnit.Symbol = parentUnit.getSymbol();
98             m_cacheUnit.Value = 1;
99             m_cacheUnit.NbDecimal = unitObject.getNumberDecimal();
100         } else {
101             m_cacheUnit.Name = QLatin1String("");
102             m_cacheUnit.Symbol = QLatin1String("");
103             m_cacheUnit.Value = 1;
104             m_cacheUnit.NbDecimal = unitObject.getNumberDecimal();
105         }
106         if (m_cacheUnit.Symbol.isEmpty()) {
107             m_cacheUnit.Symbol = ' ';
108         }
109 
110         // Bug 209672 vvvv
111         if (unitObject.getType() == SKGUnitObject::INDEX) {
112             m_cacheUnit.Symbol = ' ';
113             m_cacheUnit.Name = ' ';
114         }
115         // Bug 209672 ^^^^
116     }
117 
118     if (m_operationTable) {
119         // Read Setting
120         KSharedConfigPtr config = KSharedConfig::openConfig();
121         KConfigGroup pref = config->group("skrooge_operation");
122         m_fontFutureOperationsColor = QVariant::fromValue(pref.readEntry("fontFutureColor", QColor(Qt::gray)));
123         m_fontNotValidatedOperationsColor = QVariant::fromValue(pref.readEntry("fontNotValidatedColor", QColor(Qt::blue)));
124         m_fontSubOperationsColor = QVariant::fromValue(pref.readEntry("fontSubOperationColor", QColor(Qt::darkGreen)));
125     }
126 
127     if (m_recurrentoperationTable) {
128         // Read Setting
129         KSharedConfigPtr config = KSharedConfig::openConfig();
130         KConfigGroup pref = config->group("skrooge_scheduled");
131         m_fontDisabledScheduleColor = QVariant::fromValue(pref.readEntry("fontFutureColor", QColor(Qt::gray)));
132     }
133 
134     m_iconFavorite = SKGServices::fromTheme(QStringLiteral("bookmarks"));
135 
136     if (m_operationTable || m_recurrentoperationTable) {
137         m_iconTransfer = SKGServices::fromTheme(QStringLiteral("exchange-positions"));
138         QStringList overlays;
139         overlays.push_back(QStringLiteral("list-remove"));
140         m_iconGroup = SKGServices::fromTheme(QStringLiteral("exchange-positions"), overlays);
141         m_iconSplit = SKGServices::fromTheme(QStringLiteral("split"));
142         m_iconImported = SKGServices::fromTheme(QStringLiteral("utilities-file-archiver"));
143         {
144             QStringList overlay;
145             overlay.push_back(QStringLiteral("dialog-ok"));
146             m_iconImportedChecked = SKGServices::fromTheme(QStringLiteral("utilities-file-archiver"), overlay);
147         }
148 
149         m_iconRecurrent = SKGServices::fromTheme(QStringLiteral("chronometer"));
150         {
151             QStringList overlay;
152             overlay.push_back(QStringLiteral("bookmarks"));
153             m_iconRecurrentMaster = SKGServices::fromTheme(QStringLiteral("chronometer"), overlay);
154         }
155     }
156 
157     if (m_budgetTable) {
158         m_iconGreen = SKGServices::fromTheme(QStringLiteral("security-high"));
159         m_iconRed = SKGServices::fromTheme(QStringLiteral("security-low"));
160         m_iconAnber = SKGServices::fromTheme(QStringLiteral("security-medium"));
161     }
162 
163     if (m_unitTable) {
164         m_iconMuchMore = SKGServices::fromTheme(QStringLiteral("skrooge_much_more"));
165         m_iconMuchLess = SKGServices::fromTheme(QStringLiteral("skrooge_much_less"));
166         m_iconMore = SKGServices::fromTheme(QStringLiteral("skrooge_more"));
167         m_iconLess = SKGServices::fromTheme(QStringLiteral("skrooge_less"));
168     }
169 
170     if (m_ruleTable) {
171         m_iconSearch = SKGServices::fromTheme(QStringLiteral("edit-find"));
172         m_iconUpdate = SKGServices::fromTheme(QStringLiteral("view-refresh"));
173         m_iconAlarm = SKGServices::fromTheme(QStringLiteral("dialog-warning"));
174         m_iconTemplate = SKGServices::fromTheme(QStringLiteral("edit-guides"));
175     }
176     m_iconClosed = SKGServices::fromTheme(QStringLiteral("dialog-close"));
177 
178     if (m_categoryTable) {
179         m_iconCategory = SKGServices::fromTheme(QStringLiteral("view-categories"));
180         m_iconCategoryMoins = SKGServices::fromTheme(QStringLiteral("view-categories-expenditures"));
181         m_iconCategoryPlus = SKGServices::fromTheme(QStringLiteral("view-categories-incomes"));
182     }
183 }
184 
~SKGObjectModel()185 SKGObjectModel::~SKGObjectModel()
186 {
187     SKGTRACEINFUNC(1)
188 }
189 
headerData(int section,Qt::Orientation orientation,int role) const190 QVariant SKGObjectModel::headerData(int section, Qt::Orientation orientation, int role) const
191 {
192     _SKGTRACEINFUNC(10)
193 
194     if (orientation == Qt::Horizontal) {
195         if (role == Qt::DisplayRole || role == Qt::ToolTipRole) {
196             QString att;
197             if (section >= 0 && section < m_listAttibutes.count()) {
198                 att = m_listAttibutes[section];
199             } else {
200                 att = SKGServices::intToString(section);
201             }
202 
203             if (att == QStringLiteral("t_bookmarked") || att == QStringLiteral("i_NBRECURRENT") || att == QStringLiteral("t_status") || att == QStringLiteral("t_close") || att == QStringLiteral("t_imported")) {
204                 return (role == Qt::ToolTipRole) ? SKGObjectModelBase::headerData(section, orientation, Qt::DisplayRole) : "";
205             }
206         }
207     }
208     return SKGObjectModelBase::headerData(section, orientation, role);
209 }
210 
getAttributeForGrouping(const SKGObjectBase & iObject,const QString & iAttribute) const211 QString SKGObjectModel::getAttributeForGrouping(const SKGObjectBase& iObject, const QString& iAttribute) const
212 {
213     if (m_recurrentoperationTable && iAttribute == QStringLiteral("i_nb_times")) {
214         if (iObject.getAttribute(QStringLiteral("t_times")) != QStringLiteral("Y")) {
215             return QChar(8734);
216         }
217     } else if (m_ruleTable && iAttribute == QStringLiteral("t_action_type")) {
218         QString val = iObject.getAttribute(iAttribute);
219         if (val == QStringLiteral("S")) {
220             val = i18nc("Noun, a search", "Search");
221         } else if (val == QStringLiteral("U")) {
222             val = i18nc("Noun, a modification", "Update");
223         } else {
224             val = i18nc("Noun, an alarm", "Alarm");
225         }
226         return val;
227     } else if (iAttribute == QStringLiteral("t_bookmarked") || iAttribute == QStringLiteral("t_close")) {
228         QString val = iObject.getAttribute(iAttribute);
229         return val == QStringLiteral("Y") ? i18n("Yes") : i18n("No");
230     } else if (iAttribute == QStringLiteral("t_status")) {
231         QString val = iObject.getAttribute(iAttribute);
232         return val == QStringLiteral("N") ? i18n("None") : val == QStringLiteral("P") ? i18n("Pointed") : i18n("Checked");
233     }
234     return SKGObjectModelBase::getAttributeForGrouping(iObject, iAttribute);
235 }
236 
computeData(const QModelIndex & iIndex,int iRole) const237 QVariant SKGObjectModel::computeData(const QModelIndex& iIndex, int iRole) const
238 {
239     if (!iIndex.isValid()) {
240         return QVariant();
241     }
242     _SKGTRACEINFUNC(10)
243     SKGObjectBase* obj = getObjectPointer(iIndex);
244     if (obj == nullptr || obj->getTable().isEmpty()) {
245         return SKGObjectModelBase::computeData(iIndex, iRole);
246     }
247 
248     switch (iRole) {
249     case Qt::DisplayRole:
250     case Qt::EditRole:
251     case Qt::UserRole: {
252         QString att = m_listAttibutes[iIndex.column()];
253         QString val = obj->getAttribute(att);
254         if (att == QStringLiteral("i_NBRECURRENT")) {
255             if (iRole == Qt::UserRole) {
256                 if (val != QStringLiteral("0")) {
257                     return QLatin1String("Y");
258                 }
259                 if (obj->getAttribute(QStringLiteral("r_recurrentoperation_id")) != QStringLiteral("0")) {
260                     return QLatin1String("Y");
261                 }
262                 return QLatin1String("N");
263             }
264             return "";
265         }
266         if (att == QStringLiteral("t_bookmarked") ||
267             att == QStringLiteral("t_status") ||
268             att == QStringLiteral("t_imported") ||
269             att == QStringLiteral("t_close") ||
270             att == QStringLiteral("t_action_type")
271            ) {
272             if (iRole == Qt::UserRole) {
273                 if (m_ruleTable && att == QStringLiteral("t_action_type")) {
274                     if (val == QStringLiteral("S")) {
275                         return i18nc("Noun, a search", "Search");
276                     }
277                     if (val == QStringLiteral("U")) {
278                         return i18nc("Noun, a modification", "Update");
279                     }
280                     return i18nc("Noun, an alarm", "Alarm");
281                 }
282                 return val;
283             }
284             return "";
285         }
286         if (m_interestTable && att == QStringLiteral("t_expenditure_value_date_mode")) {
287             if (val == QStringLiteral("0")) {
288                 return i18nc("Noun", "Day -0");
289             }
290             if (val == QStringLiteral("1")) {
291                 return i18nc("Noun", "Day -1");
292             }
293             if (val == QStringLiteral("2")) {
294                 return i18nc("Noun", "Day -2");
295             }
296             if (val == QStringLiteral("3")) {
297                 return i18nc("Noun", "Day -3");
298             }
299             if (val == QStringLiteral("4")) {
300                 return i18nc("Noun", "Day -4");
301             }
302             if (val == QStringLiteral("5")) {
303                 return i18nc("Noun", "Day -5");
304             }
305             return i18nc("Noun", "Fifteen");
306         }
307         if (m_accountTable && att == QStringLiteral("d_reconciliationdate")) {
308             if (val.isEmpty() && iRole == Qt::DisplayRole) {
309                 return i18nc("Noun", "Never");
310             }
311         } else if (m_interestTable && att == QStringLiteral("t_income_value_date_mode")) {
312             if (val == QStringLiteral("0")) {
313                 return i18nc("Noun", "Day +0");
314             }
315             if (val == QStringLiteral("1")) {
316                 return i18nc("Noun", "Day +1");
317             }
318             if (val == QStringLiteral("2")) {
319                 return i18nc("Noun", "Day +2");
320             }
321             if (val == QStringLiteral("3")) {
322                 return i18nc("Noun", "Day +3");
323             }
324             if (val == QStringLiteral("4")) {
325                 return i18nc("Noun", "Day +4");
326             }
327             if (val == QStringLiteral("5")) {
328                 return i18nc("Noun", "Day +5");
329             }
330             return i18nc("Noun", "Fifteen");
331         }
332         if (getAttributeType(iIndex.column()) == SKGServices::FLOAT) {
333             double dval = SKGServices::stringToDouble(val);
334             if (iRole == Qt::DisplayRole) {
335                 if (val.isEmpty()) {
336                     return "";
337                 }
338                 if (att.endsWith(QLatin1String("_INCOME")) ||
339                     att.endsWith(QLatin1String("_EXPENSE")) ||
340                     (m_operationTable && obj->getAttribute(QStringLiteral("t_template")) == QStringLiteral("Y")) ||
341                     (m_categoryTable && (att == QStringLiteral("f_REALCURRENTAMOUNT") ||
342                                          att == QStringLiteral("f_SUMCURRENTAMOUNT")))) {
343                     if (dval == 0) {
344                         return "";
345                     }
346                 }
347 
348                 SKGServices::SKGUnitInfo unit;
349                 unit.Symbol = QLatin1String("");
350                 unit.NbDecimal = 2;
351                 if (!att.contains(QStringLiteral("QUANTITY")) && !att.contains(QStringLiteral("f_BALANCE_ENTERED"))) {
352                     unit = qobject_cast<SKGDocumentBank*>(getDocument())->getPrimaryUnit();
353                     if (m_unitvalueTable && !m_cacheUnit.Symbol.isEmpty()) {
354                         unit = m_cacheUnit;
355                     }
356                 } else {
357                     unit.NbDecimal = SKGServices::stringToInt(obj->getAttribute(QStringLiteral("i_NBDEC")));
358                     if (unit.NbDecimal == 0) {
359                         unit.NbDecimal = 2;
360                     }
361                     if (att != QStringLiteral("f_QUANTITYOWNED")) {
362                         unit.Symbol = obj->getAttribute(QStringLiteral("t_UNIT"));
363                     }
364                 }
365 
366                 // Bug 209672 vvvv
367                 if (m_unitTable) {
368                     if (obj->getAttribute(QStringLiteral("t_type")) == QStringLiteral("I")) {
369                         unit.Symbol = ' ';
370                     }
371                 }
372                 // Bug 209672 ^^^
373 
374                 if (QString::compare(att, QStringLiteral("f_rate"), Qt::CaseInsensitive) == 0) {
375                     unit.Symbol = '%';
376                     unit.NbDecimal = 2;
377                 } else if (att == QStringLiteral("f_coef")) {
378                     unit.Symbol = QLatin1String("");
379                     unit.NbDecimal = 2;
380                 }
381 
382                 if (unit.Symbol.isEmpty()) {
383                     unit.Symbol = ' ';
384                 }
385 
386                 return SKGServices::toCurrencyString(dval, unit.Symbol, unit.NbDecimal);
387             }
388             return dval;
389         }
390         if (getAttributeType(iIndex.column()) == SKGServices::INTEGER) {
391             if (m_recurrentoperationTable && att == QStringLiteral("i_nb_times")) {
392                 QString t_times = obj->getAttribute(QStringLiteral("t_times"));
393                 if (t_times != QStringLiteral("Y")) {
394                     return QChar(8734);
395                 }
396             } else if ((att == QStringLiteral("i_NBOPERATIONS") || att == QStringLiteral("i_SUMNBOPERATIONS")) && val == QStringLiteral("0")) {
397                 return "";
398             }
399 
400             return SKGServices::stringToInt(val);
401         }
402         if (m_suboperationTable && att.startsWith(QLatin1String("p_"))) {
403             val = obj->getProperty(att.right(att.count() - 2));
404             if (val.isEmpty()) {
405                 val = obj->getDocument()->getParameter(att.right(att.count() - 2), obj->getAttribute(QStringLiteral("i_OPID")) % "-operation");
406             }
407             return val;
408         }
409         if (m_payeeTable && att == QStringLiteral("t_CATEGORY") && val.isEmpty()) {
410             auto c = qobject_cast<SKGDocumentBank*>(getDocument())->getCategoryForPayee(obj->getAttribute(QStringLiteral("t_name")));
411             if (!c.isEmpty()) {
412                 return i18n("Auto: %1", c);
413             }
414         }
415         break;
416     }
417     case Qt::DecorationRole: {
418         // decoration
419         QString att = m_listAttibutes[iIndex.column()];
420         if (att == QStringLiteral("t_bookmarked")) {
421             if (obj->getAttribute(QStringLiteral("t_bookmarked")) == QStringLiteral("Y")) {
422                 return m_iconFavorite;
423             }
424         } else if (m_operationTable || m_recurrentoperationTable) {
425             if (att == QStringLiteral("t_mode")) {
426                 if (obj->getAttribute(QStringLiteral("t_TRANSFER")) == QStringLiteral("Y")) {
427                     return m_iconTransfer;
428                 }
429                 if (obj->getAttribute(QStringLiteral("i_group_id")) != QStringLiteral("0")) {
430                     return m_iconGroup;
431                 }
432             } else if (att == QStringLiteral("t_CATEGORY")) {
433                 if (SKGServices::stringToInt(obj->getAttribute(QStringLiteral("i_NBSUBOPERATIONS"))) > 1) {
434                     return m_iconSplit;
435                 }
436             } else if (att == QStringLiteral("i_NBRECURRENT") && m_operationTable) {
437                 if (obj->getAttribute(QStringLiteral("i_NBRECURRENT")) != QStringLiteral("0")) {
438                     return m_iconRecurrentMaster;
439                 }
440                 if (obj->getAttribute(QStringLiteral("r_recurrentoperation_id")) != QStringLiteral("0")) {
441                     return m_iconRecurrent;
442                 }
443             } else if (att == QStringLiteral("t_imported")) {
444                 QString impStatus = obj->getAttribute(QStringLiteral("t_imported"));
445                 if (impStatus == QStringLiteral("Y")) {
446                     return m_iconImported;
447                 }
448                 if (impStatus == QStringLiteral("P")) {
449                     return m_iconImportedChecked;
450                 }
451             } else if (att == QStringLiteral("t_REFUND") || att == QStringLiteral("t_REALREFUND") || att == QStringLiteral("t_REFUNDDISPLAY")) {
452                 if (att == QStringLiteral("t_REFUNDDISPLAY")) {
453                     if (obj->getAttribute(att).count(QStringLiteral("(")) > 1) {
454                         att.clear();
455                     } else {
456                         att = QStringLiteral("t_REFUND");
457                     }
458                 }
459                 if (!att.isEmpty()) {
460                     QString trackerName;
461                     trackerName = obj->getAttribute(att);
462                     if (!trackerName.isEmpty()) {
463                         SKGTrackerObject tracker(SKGObjectBase(getDocument(), QStringLiteral("refund")));  // Better performance if v_refund is not used
464                         tracker.setName(trackerName);
465                         tracker.load();
466 
467                         if (tracker.isClosed()) {
468                             return m_iconClosed;
469                         }
470                     }
471                 }
472             }
473         } else if (m_ruleTable) {
474             if (att == QStringLiteral("t_action_type")) {
475                 QString val = obj->getAttribute(att);
476                 if (val == QStringLiteral("S")) {
477                     return m_iconSearch;
478                 }
479                 if (val == QStringLiteral("U")) {
480                     return m_iconUpdate;
481                 }
482                 if (val == QStringLiteral("T")) {
483                     return m_iconTemplate;
484                 }
485                 return m_iconAlarm;
486             }
487         } else if (m_unitTable) {
488             if (att == QStringLiteral("f_CURRENTAMOUNT")) {
489                 SKGUnitObject unit(*obj);
490                 double amountOneYearBefore = unit.getAmount(QDate::currentDate().addYears(-1));
491                 double annualchange = 100.0 * (unit.getAmount() - amountOneYearBefore) / amountOneYearBefore;
492                 if (annualchange >= 15) {
493                     return m_iconMuchMore;
494                 }
495                 if (annualchange <= -15) {
496                     return m_iconMuchLess;
497                 }
498                 if (annualchange >= 5) {
499                     return m_iconMore;
500                 }
501                 if (annualchange <= -5) {
502                     return m_iconLess;
503                 }
504             }
505         } else if (m_accountTable) {
506             if (att == QStringLiteral("t_BANK")) {
507                 QString t_icon = obj->getAttribute(QStringLiteral("t_icon"));
508                 if (t_icon.isEmpty()) {
509                     t_icon = obj->getAttribute(QStringLiteral("t_ICON"));
510                 }
511                 if (!t_icon.isEmpty()) {
512                     QString fileName = QStandardPaths::locate(QStandardPaths::GenericDataLocation, "skrooge/images/logo/" % t_icon);
513                     if (fileName.isEmpty()) {
514                         fileName = t_icon;
515                     }
516                     return QVariant::fromValue(QIcon(fileName));
517                 }
518             } else if (att == QStringLiteral("f_importbalance")) {
519                 QString val = obj->getAttribute(att);
520                 if (val.isEmpty()) {
521                     return "";
522                 }
523 
524                 // Compute value
525                 SKGAccountObject act(*obj);
526                 auto soluces = act.getPossibleReconciliations(SKGServices::stringToDouble(val), false);
527                 return SKGServices::fromTheme(soluces.isEmpty() ? QStringLiteral("security-low") : QStringLiteral("security-high"));
528             } else if (att == QStringLiteral("f_reconciliationbalance")) {
529                 QString val = obj->getAttribute(att);
530                 if (val.isEmpty()) {
531                     return "";
532                 }
533 
534                 // Compute value
535                 SKGAccountObject act(*obj);
536                 auto soluces = act.getPossibleReconciliations(SKGServices::stringToDouble(val), false);
537                 return SKGServices::fromTheme(soluces.isEmpty() ? QStringLiteral("security-low") : QStringLiteral("security-high"));
538             }
539         } else if (m_categoryTable) {
540             if (iIndex.column() == 0) {
541                 QString t_TYPEEXPENSE = obj->getAttribute(QStringLiteral("t_TYPEEXPENSE"));
542                 if (t_TYPEEXPENSE == QStringLiteral("-")) {
543                     return m_iconCategoryMoins;
544                 }
545                 if (t_TYPEEXPENSE == QStringLiteral("+")) {
546                     return m_iconCategoryPlus;
547                 }
548                 return m_iconCategory;
549             }
550         } else if (m_budgetTable) {
551             if (att == QStringLiteral("f_DELTA") || att == QStringLiteral("f_DELTABEFORETRANSFER")) {
552                 double val = SKGServices::stringToDouble(obj->getAttribute(att));
553                 if (val < -EPSILON) {
554                     return m_iconRed;
555                 }
556                 if (val > EPSILON) {
557                     return m_iconGreen;
558                 }
559                 double transferred = SKGServices::stringToDouble(obj->getAttribute(QStringLiteral("f_transferred")));
560                 if (transferred < -EPSILON) {
561                     return m_iconAnber;
562                 }
563                 return m_iconGreen;
564             }
565         }
566         break;
567     }
568     case Qt::TextColorRole: {
569         // Text color
570         QString att = m_listAttibutes[iIndex.column()];
571         if (m_recurrentoperationTable) {
572             if (obj->getAttribute(QStringLiteral("i_nb_times")) == QStringLiteral("0")) {
573                 return  m_fontDisabledScheduleColor;
574             }
575             if (att == QStringLiteral("d_date") && SKGServices::stringToTime(obj->getAttribute(QStringLiteral("d_date"))).date() <= QDate::currentDate()) {
576                 return m_fontNegativeColor;
577             }
578         } else if (m_operationTable) {
579             if (SKGServices::stringToTime(obj->getAttribute(QStringLiteral("d_date"))).date() > QDate::currentDate()) {
580                 return m_fontFutureOperationsColor;
581             }
582             if (getAttributeType(iIndex.column()) != SKGServices::FLOAT) {
583                 if (obj->getAttribute(QStringLiteral("t_imported")) == QStringLiteral("P")) {
584                     return  m_fontNotValidatedOperationsColor;
585                 }
586                 if (m_suboperationTable) {
587                     return m_fontSubOperationsColor;
588                 }
589             }
590         }
591         break;
592     }
593     case Qt::TextAlignmentRole: {
594         // Text alignment
595         if (m_recurrentoperationTable) {
596             QString att = m_listAttibutes[iIndex.column()];
597 
598             if (att == QStringLiteral("i_auto_write_days") || att == QStringLiteral("i_warn_days") || att == QStringLiteral("i_nb_times")) {
599                 return static_cast<int>(Qt::AlignVCenter | Qt::AlignLeft);
600             }
601         }
602         break;
603     }
604     case Qt::CheckStateRole: {
605         // CheckState
606         QString att = m_listAttibutes[iIndex.column()];
607         if (m_operationTable && att == QStringLiteral("t_status")) {
608             QString cond = obj->getAttribute(QStringLiteral("t_status"));
609             if (cond == QStringLiteral("P")) {
610                 return static_cast<int>(Qt::PartiallyChecked);
611             }
612             if (cond == QStringLiteral("Y")) {
613                 return static_cast<int>(Qt::Checked);
614             }
615             if (cond == QStringLiteral("N")) {
616                 return static_cast<int>(Qt::Unchecked);
617             }
618         } else if (att == QStringLiteral("t_close")) {
619             QString cond = obj->getAttribute(QStringLiteral("t_close"));
620             if (cond == QStringLiteral("Y")) {
621                 return static_cast<int>(Qt::Checked);
622             }
623             return static_cast<int>(Qt::Unchecked);
624 
625         } else  if (m_recurrentoperationTable && att == QStringLiteral("i_auto_write_days")) {
626             QString cond = obj->getAttribute(QStringLiteral("t_auto_write"));
627             if (cond == QStringLiteral("Y")) {
628                 return static_cast<int>(Qt::Checked);
629             }
630             return static_cast<int>(Qt::Unchecked);
631 
632         } else  if (m_recurrentoperationTable && att == QStringLiteral("i_warn_days")) {
633             QString cond = obj->getAttribute(QStringLiteral("t_warn"));
634             if (cond == QStringLiteral("Y")) {
635                 return static_cast<int>(Qt::Checked);
636             }
637             return static_cast<int>(Qt::Unchecked);
638 
639         } else  if (m_recurrentoperationTable && att == QStringLiteral("i_nb_times")) {
640             QString cond = obj->getAttribute(QStringLiteral("t_times"));
641             if (cond == QStringLiteral("Y")) {
642                 return static_cast<int>(Qt::Checked);
643             }
644             return static_cast<int>(Qt::Unchecked);
645 
646         } else  if (m_budgetRuleTable && att == QStringLiteral("t_CATEGORYCONDITION")) {
647             QString cond = obj->getAttribute(QStringLiteral("t_category_condition"));
648             if (cond == QStringLiteral("Y")) {
649                 return static_cast<int>(Qt::Checked);
650             }
651             return static_cast<int>(Qt::Unchecked);
652 
653         } else  if (m_budgetRuleTable && att == QStringLiteral("i_year")) {
654             QString cond = obj->getAttribute(QStringLiteral("t_year_condition"));
655             if (cond == QStringLiteral("Y")) {
656                 return static_cast<int>(Qt::Checked);
657             }
658             return static_cast<int>(Qt::Unchecked);
659 
660         } else  if (m_budgetRuleTable && att == QStringLiteral("i_month")) {
661             QString cond = obj->getAttribute(QStringLiteral("t_month_condition"));
662             if (cond == QStringLiteral("Y")) {
663                 return static_cast<int>(Qt::Checked);
664             }
665             return static_cast<int>(Qt::Unchecked);
666 
667         } else  if (m_budgetRuleTable && att == QStringLiteral("t_CATEGORY")) {
668             QString cond = obj->getAttribute(QStringLiteral("t_category_target"));
669             if (cond == QStringLiteral("Y")) {
670                 return static_cast<int>(Qt::Checked);
671             }
672             return static_cast<int>(Qt::Unchecked);
673 
674         } else  if (m_budgetTable && att == QStringLiteral("t_CATEGORY")) {
675             QString cond = obj->getAttribute(QStringLiteral("t_including_subcategories"));
676             if (cond == QStringLiteral("Y")) {
677                 return static_cast<int>(Qt::Checked);
678             }
679             return static_cast<int>(Qt::Unchecked);
680         }
681         break;
682     }
683     case Qt::ToolTipRole: {
684         // Tooltip
685         QString toolTipString;
686         QString att = m_listAttibutes[iIndex.column()];
687         if (getAttributeType(iIndex.column()) == SKGServices::FLOAT) {
688             // Add secondary unit
689             if (!att.contains(QStringLiteral("QUANTITY")) && att != QStringLiteral("f_coef") && att != QStringLiteral("f_rate")) {
690                 SKGServices::SKGUnitInfo secondaryUnit = qobject_cast<SKGDocumentBank*>(getDocument())->getSecondaryUnit();
691                 if (!secondaryUnit.Symbol.isEmpty()) {
692                     double val = SKGServices::stringToDouble(obj->getAttribute(att));
693                     if ((!att.endsWith(QLatin1String("_INCOME")) && !att.endsWith(QLatin1String("_EXPENSE"))) || val != 0) {
694                         if (secondaryUnit.Value != 0.0) {
695                             toolTipString = SKGServices::toCurrencyString(val / secondaryUnit.Value, secondaryUnit.Symbol, secondaryUnit.NbDecimal);
696                         }
697                     }
698                 }
699             }
700 
701             // Add balance
702             if (m_operationTable && !m_suboperationTable) {
703                 SKGOperationObject op(*obj);
704                 if (!op.isTemplate()) {
705                     SKGServices::SKGUnitInfo primaryUnit = qobject_cast<SKGDocumentBank*>(getDocument())->getPrimaryUnit();
706 
707                     // Add original amount
708                     QString originalAmount = obj->getProperty(QStringLiteral("SKG_OP_ORIGINAL_AMOUNT"));
709                     if (!originalAmount.isEmpty()) {
710                         if (!toolTipString.isEmpty()) {
711                             toolTipString += QStringLiteral("\n\n");
712                         }
713                         double val1 = SKGServices::stringToDouble(obj->getAttribute(QStringLiteral("f_CURRENTAMOUNT")));
714                         double val2 = SKGServices::stringToDouble(originalAmount);
715                         double gain = (val2 != 0 ? 100.0 * (val1 - val2) / val2 : 0);
716                         double gainperyear = gain;
717 
718                         int nbDays = op.getDate().daysTo(QDate::currentDate());
719                         if (nbDays != 0 && val2 != 0) {
720                             double gainperday = 100.0 * expm1(log(val1 / val2) / static_cast<double>(nbDays));
721                             gainperyear = 100.0 * (pow(1.0 + gainperday / 100.0, 365.0) - 1);
722                         }
723                         toolTipString += i18nc("Noun", "Original amount=%1 (%2 = %3 / year)",
724                                                SKGServices::toCurrencyString(val2, primaryUnit.Symbol, primaryUnit.NbDecimal),
725                                                (gain >= 0 ? "+" : "-") % SKGServices::toPercentageString(gain, 2),
726                                                (gainperyear >= 0 ? "+" : "-") % SKGServices::toPercentageString(gainperyear, 2));
727                     } else {
728                         if (!toolTipString.isEmpty()) {
729                             toolTipString += QStringLiteral("\n\n");
730                         }
731                         double val1 = SKGServices::stringToDouble(obj->getAttribute(QStringLiteral("f_CURRENTAMOUNT")));
732                         double val2 = op.getAmount(op.getDate());
733                         double gain = (val2 != 0 ? 100.0 * (val1 - val2) / val2 : 0);
734                         double gainperyear = gain;
735 
736                         int nbDays = op.getDate().daysTo(QDate::currentDate());
737                         if (nbDays != 0 && val2 != 0) {
738                             double gainperday = 100.0 * expm1(log(val1 / val2) / static_cast<double>(nbDays));
739                             gainperyear = 100.0 * (pow(1.0 + gainperday / 100.0, 365.0) - 1);
740                         }
741 
742                         QString sval1 = SKGServices::toCurrencyString(val1, primaryUnit.Symbol, primaryUnit.NbDecimal);
743                         QString sval2 = SKGServices::toCurrencyString(val2, primaryUnit.Symbol, primaryUnit.NbDecimal);
744                         if (sval1 != sval2) {
745                             toolTipString += i18nc("Noun", "Amount at creation date=%1 (%2 = %3 / year)",
746                                                    sval2,
747                                                    (gain >= 0 ? "+" : "-") % SKGServices::toPercentageString(gain, 2),
748                                                    (gainperyear >= 0 ? "+" : "-") % SKGServices::toPercentageString(gainperyear, 2));
749 
750                             toolTipString += '\n';
751                         }
752                     }
753                     toolTipString += i18nc("Noun", "Account balance=%1",
754                                            SKGServices::toCurrencyString(op.getBalance(), primaryUnit.Symbol, primaryUnit.NbDecimal));
755                 }
756             }
757 
758             if (m_budgetTable) {
759                 if (att == QStringLiteral("f_DELTA")) {
760                     double val = SKGServices::stringToDouble(obj->getAttribute(QStringLiteral("f_DELTABEFORETRANSFER")));
761 
762                     if (!toolTipString.isEmpty()) {
763                         toolTipString += QStringLiteral("\n\n");
764                     }
765 
766                     SKGServices::SKGUnitInfo primaryUnit = qobject_cast<SKGDocumentBank*>(getDocument())->getPrimaryUnit();
767                     toolTipString += i18nc("Noun", "Original delta=%1",
768                                            SKGServices::toCurrencyString(val, primaryUnit.Symbol, primaryUnit.NbDecimal));
769                 } else if (att == QStringLiteral("f_budgeted_modified")) {
770                     QString reasons = obj->getAttribute(QStringLiteral("t_modification_reasons"));
771                     if (!reasons.isEmpty()) {
772                         if (!toolTipString.isEmpty()) {
773                             toolTipString += QStringLiteral("\n\n");
774                         }
775                         toolTipString += reasons;
776                     }
777                 }
778             }
779 
780             if (m_unitTable) {
781                 if (att == QStringLiteral("f_CURRENTAMOUNT")) {
782                     SKGUnitObject unit(*obj);
783                     double amountOneYearBefore = unit.getAmount(QDate::currentDate().addYears(-1));
784                     double annualchange = 100.0 * (unit.getAmount() - amountOneYearBefore) / amountOneYearBefore;
785                     if (!toolTipString.isEmpty()) {
786                         toolTipString += QStringLiteral("\n\n");
787                     }
788                     toolTipString += SKGServices::toPercentageString(annualchange);
789                 }
790             }
791         } else if (m_operationTable || m_recurrentoperationTable) {
792             if (att == QStringLiteral("t_imported")) {
793                 if (!m_suboperationTable) {
794                     SKGOperationObject op;
795                     if (m_recurrentoperationTable) {
796                         SKGRecurrentOperationObject rop(*obj);
797                         rop.getParentOperation(op);
798                     } else {
799                         op = *obj;
800                     }
801                     toolTipString = op.getImportID();
802                 }
803             } else if (att == QStringLiteral("t_REFUND") || att == QStringLiteral("t_REALREFUND") || att == QStringLiteral("t_REFUNDDISPLAY")) {
804                 if (att == QStringLiteral("t_REFUNDDISPLAY")) {
805                     if (obj->getAttribute(att).count(QStringLiteral("(")) > 1) {
806                         att.clear();
807                     } else {
808                         att = QStringLiteral("t_REFUND");
809                     }
810                 }
811                 if (!att.isEmpty()) {
812                     QString trackerName = obj->getAttribute(att);
813                     if (!trackerName.isEmpty()) {
814                         SKGTrackerObject tracker(getDocument());
815                         tracker.setName(trackerName);
816                         tracker.load();
817                         SKGServices::SKGUnitInfo unit = qobject_cast<SKGDocumentBank*>(getDocument())->getPrimaryUnit();
818                         toolTipString = SKGServices::toCurrencyString(tracker.getCurrentAmount(), unit.Symbol, unit.NbDecimal);
819                     }
820                 }
821             } else if (att == QStringLiteral("t_PAYEE")) {
822                 QString payeeName = obj->getAttribute(att);
823                 if (!payeeName.isEmpty()) {
824                     SKGPayeeObject payee(getDocument());
825                     payee.setName(payeeName);
826                     payee.load();
827 
828                     auto address = payee.getAddress();
829                     if (!address.isEmpty()) {
830                         toolTipString += i18nc("Information", "Address= %1\n", address);
831                     }
832 
833                     auto c = payee.getAttribute(QStringLiteral("t_CATEGORY"));
834                     if (c.isEmpty()) {
835                         c = qobject_cast<SKGDocumentBank*>(getDocument())->getCategoryForPayee(payeeName, false);
836                     }
837                     if (!c.isEmpty()) {
838                         toolTipString += i18nc("Information", "Category= %1\n", c);
839                     }
840                 }
841             }  else if (att == QStringLiteral("t_ACCOUNT")  || att == QStringLiteral("t_TOACCOUNT")) {
842                 QString accountName = obj->getAttribute(att);
843                 if (!accountName.isEmpty()) {
844                     SKGAccountObject account(getDocument());
845                     account.setName(accountName);
846                     account.load();
847 
848                     auto tmp = account.getAgencyNumber();
849                     if (!tmp.isEmpty()) {
850                         toolTipString += i18nc("Information", "Agency number= %1\n", tmp);
851                     }
852 
853                     tmp = account.getNumber();
854                     if (!tmp.isEmpty()) {
855                         toolTipString += i18nc("Information", "Number= %1\n", tmp);
856                     }
857 
858                     tmp = account.getAgencyAddress();
859                     if (!tmp.isEmpty()) {
860                         toolTipString += i18nc("Information", "Address= %1\n", tmp);
861                     }
862 
863                     tmp = account.getComment();
864                     if (!tmp.isEmpty()) {
865                         toolTipString += i18nc("Information", "Comment= %1\n", tmp);
866                     }
867                 }
868             } else if (att == QStringLiteral("t_CATEGORY")) {
869                 SKGOperationObject op(*obj);
870                 if (m_recurrentoperationTable) {
871                     SKGRecurrentOperationObject rop(*obj);
872                     rop.getParentOperation(op);
873                 }
874                 if (SKGServices::stringToInt(op.getAttribute(QStringLiteral("i_NBSUBOPERATIONS"))) > 1) {
875                     SKGObjectBase::SKGListSKGObjectBase subOps;
876                     op.getSubOperations(subOps);
877                     for (const auto& subOp : qAsConst(subOps)) {
878                         toolTipString += subOp.getDisplayName() % '\n';
879                     }
880                 }
881             } else if (att == QStringLiteral("t_mode")) {
882                 SKGOperationObject op(*obj);
883                 if (m_recurrentoperationTable) {
884                     SKGRecurrentOperationObject rop(*obj);
885                     rop.getParentOperation(op);
886                 }
887                 if (op.getAttribute(QStringLiteral("i_group_id")) != QStringLiteral("0")) {
888                     SKGOperationObject gop;
889                     op.getGroupOperation(gop);
890 
891                     SKGObjectBase::SKGListSKGObjectBase gOps;
892                     op.getGroupedOperations(gOps);
893                     for (const auto& item : qAsConst(gOps)) {
894                         SKGOperationObject gOp(item);
895                         SKGAccountObject account;
896                         gOp.getParentAccount(account);
897                         toolTipString += account.getDisplayName() % '\n';
898                     }
899                 }
900             }
901 
902             if (m_operationTable && !m_suboperationTable && toolTipString.isEmpty()) {
903                 SKGOperationObject op(*obj);
904                 if (op.getStatus() == SKGOperationObject::POINTED) {
905                     toolTipString = i18nc("A tool tip", "This operation is pointed but not checked yet.");
906                     toolTipString += '\n';
907                     toolTipString += i18nc("A tool tip", "You can use the reconciliation mode to validate pointed operations.");
908                     toolTipString += '\n';
909                     if (att == QStringLiteral("t_status")) {
910                         toolTipString += i18nc("A tool tip", "Click in this column to switch back status.");
911                     } else {
912                         toolTipString += i18nc("A tool tip", "Click in the Status (checkmark) column to switch back status.");
913                     }
914                     toolTipString += '\n';
915                     toolTipString += i18nc("A tool tip", "Ctrl+click to force checked status.");
916                 } else if (op.getStatus() == SKGOperationObject::CHECKED) {
917                     toolTipString = i18nc("A tool tip", "This operation is already checked.");
918                 } else if (op.getStatus() == SKGOperationObject::NONE) {
919                     toolTipString = i18nc("A tool tip", "This operation is not pointed yet.");
920                     toolTipString += '\n';
921                     toolTipString += i18nc("A tool tip", "Click to set pointed status.");
922                     toolTipString += '\n';
923                     toolTipString += i18nc("A tool tip", "Ctrl+click to force checked status.");
924                 }
925             }
926         } else if (m_ruleTable && att == QStringLiteral("t_action_type")) {
927             QString val = obj->getAttribute(att);
928             if (val == QStringLiteral("S")) {
929                 toolTipString = i18nc("Noun, a search", "Search");
930             } else if (val == QStringLiteral("U")) {
931                 toolTipString = i18nc("Noun, a modification", "Update");
932             } else {
933                 toolTipString = i18nc("Noun, an alarm", "Alarm");
934             }
935         }
936 
937         QString toolTipStringBase = SKGObjectModelBase::computeData(iIndex, iRole).toString();
938         if (!toolTipStringBase.isEmpty()) {
939             if (!toolTipString.isEmpty()) {
940                 toolTipString += QStringLiteral("\n\n");
941             }
942             toolTipString += toolTipStringBase;
943         }
944         return toolTipString;
945     }
946     default: {
947     }
948     }
949     return SKGObjectModelBase::computeData(iIndex, iRole);
950 }
951 
setData(const QModelIndex & iIndex,const QVariant & iValue,int iRole)952 bool SKGObjectModel::setData(const QModelIndex& iIndex, const QVariant& iValue, int iRole)
953 {
954     if (!iIndex.isValid()) {
955         return false;
956     }
957 
958     if (iRole == Qt::CheckStateRole) {
959         SKGError err;
960         {
961             auto newState = static_cast<Qt::CheckState>(iValue.toInt());
962             if (m_accountTable) {
963                 SKGAccountObject obj(getObject(iIndex));
964                 SKGBEGINLIGHTTRANSACTION(*getDocument(),
965                                          (newState == Qt::Checked ? i18nc("Noun, name of the user action", "Close account '%1'", obj.getName()) : i18nc("Noun, name of the user action", "Open account '%1'", obj.getName())), err);
966                 if (qAbs(obj.getCurrentAmount()) > 0.01 && newState == Qt::Checked) {
967                     err = getDocument()->sendMessage(i18nc("An information message",  "Warning, you closed an account with money"), SKGDocument::Warning);
968                 }
969                 IFOKDO(err, obj.setClosed(newState == Qt::Checked))
970                 IFOKDO(err, obj.save())
971             } else if (m_trackerTable) {
972                 SKGTrackerObject obj(getObject(iIndex));
973                 SKGBEGINLIGHTTRANSACTION(*getDocument(),
974                                          (newState == Qt::Checked ? i18nc("Noun, name of the user action", "Close tracker '%1'", obj.getName()) : i18nc("Noun, name of the user action", "Open tracker '%1'", obj.getName())), err);
975                 err = obj.setClosed(newState == Qt::Checked);
976                 IFOKDO(err, obj.save())
977             }  else if (m_categoryTable) {
978                 SKGCategoryObject obj(getObject(iIndex));
979                 SKGBEGINLIGHTTRANSACTION(*getDocument(),
980                                          (newState == Qt::Checked ? i18nc("Noun, name of the user action", "Close category '%1'", obj.getName()) : i18nc("Noun, name of the user action", "Open category '%1'", obj.getName())), err);
981                 err = obj.setClosed(newState == Qt::Checked);
982                 IFOKDO(err, obj.save())
983             }  else if (m_payeeTable) {
984                 SKGPayeeObject obj(getObject(iIndex));
985                 SKGBEGINLIGHTTRANSACTION(*getDocument(),
986                                          (newState == Qt::Checked ? i18nc("Noun, name of the user action", "Close payee '%1'", obj.getName()) : i18nc("Noun, name of the user action", "Open payee '%1'", obj.getName())), err);
987                 err = obj.setClosed(newState == Qt::Checked);
988                 IFOKDO(err, obj.save())
989             } else if (m_operationTable && !m_suboperationTable) {
990                 // Get the real object, not the object from the view
991                 SKGObjectBase* objtmp = getObjectPointer(iIndex);
992                 if (objtmp != nullptr) {
993                     SKGOperationObject obj = SKGOperationObject(objtmp->getDocument(), objtmp->getID());
994                     SKGBEGINLIGHTTRANSACTION(*getDocument(), i18nc("Noun, name of the user action", "Change operation status"), err)
995                     SKGOperationObject::OperationStatus statusinitial = obj.getStatus();
996                     SKGOperationObject::OperationStatus t_status = statusinitial;
997                     if ((QApplication::keyboardModifiers() & Qt::ControlModifier) != 0u) {
998                         // 2747379: NONE ==> CHECKED, POINTED ==> CHECKED, CHECKED ==> CHECKED
999                         t_status = SKGOperationObject::CHECKED;
1000                         // t_status= ( t_status==SKGOperationObject::POINTED ? SKGOperationObject::NONE : ( t_status==SKGOperationObject::CHECKED ? SKGOperationObject::POINTED : SKGOperationObject::NONE ) );
1001                     } else {
1002                         // 2747379: NONE ==> POINTED, POINTED ==> NONE, CHECKED ==> POINTED
1003                         t_status = (t_status == SKGOperationObject::NONE ? SKGOperationObject::POINTED : (t_status == SKGOperationObject::POINTED ? SKGOperationObject::NONE : SKGOperationObject::POINTED));
1004                         // t_status=(t_status==SKGOperationObject::POINTED ? SKGOperationObject::CHECKED : (t_status==SKGOperationObject::CHECKED ? SKGOperationObject::CHECKED : SKGOperationObject::POINTED ));
1005                     }
1006                     if (t_status != statusinitial) {
1007                         err = obj.setStatus(t_status);
1008                         IFOKDO(err, obj.save())
1009 
1010                         // Send message
1011                         IFOKDO(err, getDocument()->sendMessage(i18nc("An information to the user", "The status of the operation '%1' has been changed", obj.getDisplayName()), SKGDocument::Hidden))
1012                     }
1013                 }
1014             } else if (m_recurrentoperationTable) {
1015                 QString att = m_listAttibutes[iIndex.column()];
1016 
1017                 // Get the real object, not the object from the view
1018                 SKGObjectBase* objtmp = getObjectPointer(iIndex);
1019                 if (objtmp != nullptr) {
1020                     SKGRecurrentOperationObject obj = SKGRecurrentOperationObject(objtmp->getDocument(), objtmp->getID());
1021 
1022                     SKGBEGINLIGHTTRANSACTION(*getDocument(), i18nc("Noun, name of the user action", "Recurrent operation update"), err)
1023                     if (att == QStringLiteral("i_warn_days")) {
1024                         err = obj.warnEnabled(!obj.isWarnEnabled());
1025                     } else if (att == QStringLiteral("i_auto_write_days")) {
1026                         err = obj.autoWriteEnabled(!obj.isAutoWriteEnabled());
1027                     } else if (att == QStringLiteral("i_nb_times")) {
1028                         err = obj.timeLimit(!obj.hasTimeLimit());
1029                     }
1030                     IFOKDO(err, obj.save())
1031 
1032                     // Send message
1033                     IFOKDO(err, getDocument()->sendMessage(i18nc("An information to the user", "The recurrent operation '%1' has been updated", obj.getDisplayName()), SKGDocument::Hidden))
1034                 }
1035             } else if (m_budgetRuleTable) {
1036                 QString att = m_listAttibutes[iIndex.column()];
1037 
1038                 // Get the real object, not the object from the view
1039                 SKGObjectBase* objtmp = getObjectPointer(iIndex);
1040                 if (objtmp != nullptr) {
1041                     SKGBudgetRuleObject obj = SKGBudgetRuleObject(objtmp->getDocument(), objtmp->getID());
1042 
1043                     SKGBEGINLIGHTTRANSACTION(*getDocument(), i18nc("Noun, name of the user action", "Budget rule update"), err)
1044                     if (att == QStringLiteral("i_year")) {
1045                         err = obj.enableYearCondition(!obj.isYearConditionEnabled());
1046                     } else if (att == QStringLiteral("i_month")) {
1047                         err = obj.enableMonthCondition(!obj.isMonthConditionEnabled());
1048                     } else if (att == QStringLiteral("t_CATEGORYCONDITION")) {
1049                         err = obj.enableCategoryCondition(!obj.isCategoryConditionEnabled());
1050                     } else if (att == QStringLiteral("t_CATEGORY")) {
1051                         err = obj.enableCategoryChange(!obj.isCategoryChangeEnabled());
1052                     }
1053                     IFOKDO(err, obj.save())
1054 
1055                     // Send message
1056                     IFOKDO(err, getDocument()->sendMessage(i18nc("An information to the user", "The budget rule '%1' has been updated", obj.getDisplayName()), SKGDocument::Hidden))
1057                 }
1058             } else if (m_budgetTable) {
1059                 QString att = m_listAttibutes[iIndex.column()];
1060 
1061                 // Get the real object, not the object from the view
1062                 SKGObjectBase* objtmp = getObjectPointer(iIndex);
1063                 if (objtmp != nullptr) {
1064                     SKGBudgetObject obj = SKGBudgetObject(objtmp->getDocument(), objtmp->getID());
1065 
1066                     SKGBEGINTRANSACTION(*getDocument(), i18nc("Noun, name of the user action", "Budget update"), err)
1067                     if (att == QStringLiteral("t_CATEGORY")) {
1068                         IFOKDO(err, obj.enableSubCategoriesInclusion(!obj.isSubCategoriesInclusionEnabled()))
1069                         IFOKDO(err, obj.save())
1070 
1071                         // Send message
1072                         IFOKDO(err, getDocument()->sendMessage(i18nc("An information to the user", "The budget '%1' have been updated", obj.getDisplayName()), SKGDocument::Hidden))
1073                     }
1074                 }
1075             }
1076         }
1077 
1078         SKGMainPanel::displayErrorMessage(err);
1079         return !err;
1080     }
1081     return SKGObjectModelBase::setData(iIndex, iValue, iRole);
1082 }
1083 
flags(const QModelIndex & iIndex) const1084 Qt::ItemFlags SKGObjectModel::flags(const QModelIndex& iIndex) const
1085 {
1086     _SKGTRACEINFUNC(10)
1087 
1088     Qt::ItemFlags f = SKGObjectModelBase::flags(iIndex);
1089 
1090     if (iIndex.isValid()) {
1091         QString att = m_listAttibutes[iIndex.column()];
1092         if (att == QStringLiteral("t_bookmarked") || m_ruleTable || m_recurrentoperationTable || m_interestTable || m_interestResultTable) {
1093             f = f & ~Qt::ItemIsEditable;
1094         }
1095     }
1096 
1097     if (m_categoryTable || m_payeeTable || m_accountTable || m_unitTable || m_trackerTable) {
1098         if (iIndex.isValid()) {
1099             f |= Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled;
1100         } else {
1101             f |= Qt::ItemIsDropEnabled;
1102         }
1103     }
1104 
1105     return f;
1106 }
1107 
supportedDragActions() const1108 Qt::DropActions SKGObjectModel::supportedDragActions() const
1109 {
1110     if (m_categoryTable || m_payeeTable || m_accountTable || m_unitTable || m_trackerTable) {
1111         return Qt::MoveAction;
1112     }
1113     return SKGObjectModelBase::supportedDragActions();
1114 }
1115 
supportedDropActions() const1116 Qt::DropActions SKGObjectModel::supportedDropActions() const
1117 {
1118     return SKGObjectModelBase::supportedDropActions();
1119 }
1120 
dropMimeData(const QMimeData * iData,Qt::DropAction iAction,int iRow,int iColumn,const QModelIndex & iParent)1121 bool SKGObjectModel::dropMimeData(const QMimeData* iData,
1122                                   Qt::DropAction iAction,
1123                                   int iRow, int iColumn,
1124                                   const QModelIndex& iParent)
1125 {
1126     if (SKGObjectModelBase::dropMimeData(iData, iAction, iRow, iColumn, iParent)) {
1127         return true;
1128     }
1129     if (iAction == Qt::IgnoreAction) {
1130         return true;
1131     }
1132     if ((iData == nullptr) || !(iData->hasFormat(QStringLiteral("application/skg.category.ids")) ||
1133                                 iData->hasFormat(QStringLiteral("application/skg.payee.ids")) ||
1134                                 iData->hasFormat(QStringLiteral("application/skg.account.ids")) ||
1135                                 iData->hasFormat(QStringLiteral("application/skg.refund.ids")) ||
1136                                 iData->hasFormat(QStringLiteral("application/skg.unit.ids")))) {
1137         return false;
1138     }
1139     if (iColumn > 0) {
1140         return false;
1141     }
1142 
1143     SKGError err;
1144     if (iData->hasFormat(QStringLiteral("application/skg.category.ids"))) {
1145         QByteArray encodedData = iData->data(QStringLiteral("application/skg.category.ids"));
1146         QDataStream stream(&encodedData, QIODevice::ReadOnly);
1147         QStringList newItems;
1148 
1149         SKGCategoryObject parentCategory;
1150         if (iParent.isValid()) {
1151             parentCategory = getObject(iParent);
1152         }
1153         {
1154             SKGBEGINTRANSACTION(*getDocument(), i18nc("Noun, name of the user action", "Move category"), err)
1155 
1156             while (!stream.atEnd() && !err) {
1157                 int o_id;
1158                 QString o_table;
1159                 stream >> o_table;
1160                 stream >> o_id;
1161 
1162                 SKGCategoryObject child(getDocument(), o_id);
1163                 err = child.load();
1164                 QString oldName = child.getDisplayName();
1165                 IFOK(err) {
1166                     if (iParent.isValid()) {
1167                         err = child.setParentCategory(parentCategory);
1168                     } else {
1169                         err = child.removeParentCategory();
1170                     }
1171                 }
1172                 IFOKDO(err, child.save())
1173 
1174                 // Send message
1175                 IFOKDO(err, getDocument()->sendMessage(i18nc("An information to the user", "The category '%1' has been moved to '%2'", oldName, child.getDisplayName()), SKGDocument::Hidden))
1176             }
1177         }
1178     } else if (iData->hasFormat(QStringLiteral("application/skg.payee.ids"))) {
1179         QByteArray encodedData = iData->data(QStringLiteral("application/skg.payee.ids"));
1180         QDataStream stream(&encodedData, QIODevice::ReadOnly);
1181         QStringList newItems;
1182 
1183         if (iParent.isValid()) {
1184             SKGBEGINTRANSACTION(*getDocument(), i18nc("Noun, name of the user action", "Merge payees"), err)
1185             SKGPayeeObject parentPayee(getObject(iParent));
1186             while (!stream.atEnd() && !err) {
1187                 int o_id;
1188                 QString o_table;
1189                 stream >> o_table;
1190                 stream >> o_id;
1191 
1192                 SKGPayeeObject child(getDocument(), o_id);
1193                 err = child.load();
1194 
1195                 // Send message
1196                 IFOKDO(err, getDocument()->sendMessage(i18nc("An information to the user", "The payee '%1' has been merged with payee '%2'", child.getDisplayName(), parentPayee.getDisplayName()), SKGDocument::Hidden))
1197 
1198                 IFOKDO(err, parentPayee.merge(child))
1199             }
1200         }
1201     } else if (iData->hasFormat(QStringLiteral("application/skg.account.ids"))) {
1202         QByteArray encodedData = iData->data(QStringLiteral("application/skg.account.ids"));
1203         QDataStream stream(&encodedData, QIODevice::ReadOnly);
1204         QStringList newItems;
1205 
1206         if (iParent.isValid()) {
1207             SKGBEGINTRANSACTION(*getDocument(), i18nc("Noun, name of the user action", "Merge accounts"), err)
1208             SKGAccountObject parentAccount(getObject(iParent));
1209             while (!stream.atEnd() && !err) {
1210                 int o_id;
1211                 QString o_table;
1212                 stream >> o_table;
1213                 stream >> o_id;
1214 
1215                 SKGAccountObject child(getDocument(), o_id);
1216                 err = child.load();
1217 
1218                 // Send message
1219                 IFOKDO(err, getDocument()->sendMessage(i18nc("An information to the user", "The account '%1' has been merged with account '%2'", child.getDisplayName(), parentAccount.getDisplayName()), SKGDocument::Hidden))
1220 
1221                 double balancebefore = 0.0;
1222                 double balanceafter = 0.0;
1223                 SKGUnitObject unit2;
1224                 IFOKDO(err, parentAccount.getInitialBalance(balancebefore, unit2))
1225                 IFOKDO(err, parentAccount.merge(child, !(QApplication::keyboardModifiers() &Qt::ControlModifier)))
1226                 IFOKDO(err, parentAccount.getInitialBalance(balanceafter, unit2))
1227                 if (balanceafter != balancebefore) {
1228                     getDocument()->sendMessage(i18nc("Warning message", "The initial balance of the target account '%1' has been change to %2.\nIf you want to do the merge without changing the initial balance, you must keep the Ctrl key pressed.", parentAccount.getDisplayName(), balanceafter), SKGDocument::Warning);
1229                 }
1230             }
1231         }
1232     } else if (iData->hasFormat(QStringLiteral("application/skg.unit.ids"))) {
1233         QByteArray encodedData = iData->data(QStringLiteral("application/skg.unit.ids"));
1234         QDataStream stream(&encodedData, QIODevice::ReadOnly);
1235         QStringList newItems;
1236 
1237         if (iParent.isValid()) {
1238             SKGBEGINTRANSACTION(*getDocument(), i18nc("Noun, name of the user action", "Merge units"), err)
1239             SKGUnitObject parentUnit(getObject(iParent));
1240             while (!stream.atEnd() && !err) {
1241                 int o_id;
1242                 QString o_table;
1243                 stream >> o_table;
1244                 stream >> o_id;
1245 
1246                 SKGUnitObject child(getDocument(), o_id);
1247                 err = child.load();
1248 
1249                 // Send message
1250                 IFOKDO(err, getDocument()->sendMessage(i18nc("An information to the user", "The unit '%1' has been merged with unit '%2'", child.getDisplayName(), parentUnit.getDisplayName()), SKGDocument::Hidden))
1251 
1252                 IFOKDO(err, parentUnit.merge(child))
1253             }
1254         }
1255     } else if (iData->hasFormat(QStringLiteral("application/skg.refund.ids"))) {
1256         QByteArray encodedData = iData->data(QStringLiteral("application/skg.refund.ids"));
1257         QDataStream stream(&encodedData, QIODevice::ReadOnly);
1258         QStringList newItems;
1259 
1260         if (iParent.isValid()) {
1261             SKGBEGINTRANSACTION(*getDocument(), i18nc("Noun, name of the user action", "Merge trackers"), err)
1262             SKGTrackerObject parentTracker(getObject(iParent));
1263             while (!stream.atEnd() && !err) {
1264                 int o_id;
1265                 QString o_table;
1266                 stream >> o_table;
1267                 stream >> o_id;
1268 
1269                 SKGTrackerObject child(getDocument(), o_id);
1270                 err = child.load();
1271 
1272                 // Send message
1273                 IFOKDO(err, parentTracker.getDocument()->sendMessage(i18nc("An information to the user", "The tracker '%1' has been merged with tracker '%2'", child.getDisplayName(), parentTracker.getDisplayName()), SKGDocument::Hidden))
1274 
1275                 IFOKDO(err, parentTracker.merge(child))
1276             }
1277         }
1278     }
1279     SKGMainPanel::displayErrorMessage(err);
1280     return !err;
1281 }
1282 
dataModified(const QString & iTableName,int iIdTransaction)1283 void SKGObjectModel::dataModified(const QString& iTableName, int iIdTransaction)
1284 {
1285     if (getRealTable() == iTableName || iTableName.isEmpty() || getRealTable() == QStringLiteral("doctransaction")) {
1286         SKGTRACEINFUNC(1)
1287         if (iTableName == QStringLiteral("category")) {
1288             // Full refresh
1289             m_isResetRealyNeeded = true;
1290             refresh();
1291         } else {
1292             SKGObjectModelBase::dataModified(iTableName, iIdTransaction);
1293         }
1294     } else {
1295         SKGObjectModelBase::dataModified(iTableName, iIdTransaction);
1296     }
1297 }
1298 
formatMoney(double iValue) const1299 QString SKGObjectModel::formatMoney(double iValue) const
1300 {
1301     return getDocument()->formatMoney(iValue, qobject_cast<SKGDocumentBank*>(getDocument())->getPrimaryUnit(), false);
1302 }
1303 
1304 
1305 
1306