File indexing completed on 2024-05-05 17:18:50

0001 /***************************************************************************
0002  * SPDX-FileCopyrightText: 2022 S. MANKOWSKI stephane@mankowski.fr
0003  * SPDX-FileCopyrightText: 2022 G. DE BURE support@mankowski.fr
0004  * SPDX-License-Identifier: GPL-3.0-or-later
0005  ***************************************************************************/
0006 /** @file
0007 * This file defines classes SKGObjectModel.
0008 *
0009 * @author Stephane MANKOWSKI / Guillaume DE BURE
0010  */
0011 #include "skgobjectmodel.h"
0012 
0013 #include <math.h>
0014 
0015 #include <kconfiggroup.h>
0016 #include <klocalizedstring.h>
0017 
0018 #include <qapplication.h>
0019 #include <qcolor.h>
0020 #include <qdir.h>
0021 #include <qfont.h>
0022 #include <qicon.h>
0023 #include <qmimedata.h>
0024 #include <qstandardpaths.h>
0025 
0026 #include "skgaccountobject.h"
0027 #include "skgbudgetobject.h"
0028 #include "skgbudgetruleobject.h"
0029 #include "skgcategoryobject.h"
0030 #include "skgdocumentbank.h"
0031 #include "skgmainpanel.h"
0032 #include "skgoperationobject.h"
0033 #include "skgpayeeobject.h"
0034 #include "skgrecurrentoperationobject.h"
0035 #include "skgsuboperationobject.h"
0036 #include "skgtraces.h"
0037 #include "skgtrackerobject.h"
0038 #include "skgtransactionmng.h"
0039 #include "skgunitobject.h"
0040 #include "skgunitvalueobject.h"
0041 
0042 SKGObjectModel::
0043 SKGObjectModel(SKGDocumentBank* iDocument,
0044                const QString& iTable,
0045                const QString& iWhereClause,
0046                QWidget* iParent,
0047                const QString& iParentAttribute,
0048                bool iResetOnCreation)
0049     : SKGObjectModelBase(iDocument, iTable, iWhereClause, iParent, iParentAttribute, false)
0050 {
0051     SKGTRACEINFUNC(1)
0052 
0053     m_operationTable = false;
0054     m_recurrentoperationTable = false;
0055     m_trackerTable = false;
0056     m_accountTable = false;
0057     m_unitTable = false;
0058     m_unitvalueTable = false;
0059     m_suboperationTable = false;
0060     m_categoryTable = false;
0061     m_ruleTable = false;
0062     m_interestTable = false;
0063     m_interestResultTable = false;
0064     m_payeeTable = false;
0065     m_budgetTable = false;
0066     m_budgetRuleTable = false;
0067     m_isResetRealyNeeded = iResetOnCreation;
0068     refresh();
0069 }
0070 
0071 void SKGObjectModel::buidCache()
0072 {
0073     SKGObjectModelBase::buidCache();
0074     m_operationTable = (getRealTable() == QStringLiteral("operation") || getRealTable() == QStringLiteral("suboperation"));
0075     m_payeeTable = (getRealTable() == QStringLiteral("payee"));
0076     m_trackerTable = (getRealTable() == QStringLiteral("refund"));
0077     m_recurrentoperationTable = (getRealTable() == QStringLiteral("recurrentoperation"));
0078     m_accountTable = (getRealTable() == QStringLiteral("account"));
0079     m_unitTable = (getRealTable() == QStringLiteral("unit"));
0080     m_unitvalueTable = (getRealTable() == QStringLiteral("unitvalue"));
0081     m_suboperationTable = (getTable() == QStringLiteral("v_suboperation_consolidated"));
0082     m_ruleTable = (getRealTable() == QStringLiteral("rule"));
0083     m_categoryTable = (getRealTable() == QStringLiteral("category"));
0084     m_interestTable = (getRealTable() == QStringLiteral("interest"));
0085     m_interestResultTable = (getRealTable() == QStringLiteral("interest_result"));
0086     m_budgetTable = (getRealTable() == QStringLiteral("budget"));
0087     m_budgetRuleTable = (getRealTable() == QStringLiteral("budgetrule"));
0088 
0089     if (m_unitvalueTable) {
0090         SKGUnitValueObject unitValObject(getObject(this->index(0, 0)));
0091         SKGUnitObject unitObject;
0092         unitValObject.getUnit(unitObject);
0093         SKGUnitObject parentUnit;
0094         unitObject.getUnit(parentUnit);
0095         if (parentUnit.exist()) {
0096             m_cacheUnit.Name = parentUnit.getName();
0097             m_cacheUnit.Symbol = parentUnit.getSymbol();
0098             m_cacheUnit.Value = 1;
0099             m_cacheUnit.NbDecimal = unitObject.getNumberDecimal();
0100         } else {
0101             m_cacheUnit.Name = QLatin1String("");
0102             m_cacheUnit.Symbol = QLatin1String("");
0103             m_cacheUnit.Value = 1;
0104             m_cacheUnit.NbDecimal = unitObject.getNumberDecimal();
0105         }
0106         if (m_cacheUnit.Symbol.isEmpty()) {
0107             m_cacheUnit.Symbol = ' ';
0108         }
0109 
0110         // Bug 209672 vvvv
0111         if (unitObject.getType() == SKGUnitObject::INDEX) {
0112             m_cacheUnit.Symbol = ' ';
0113             m_cacheUnit.Name = ' ';
0114         }
0115         // Bug 209672 ^^^^
0116     }
0117 
0118     if (m_operationTable) {
0119         // Read Setting
0120         KSharedConfigPtr config = KSharedConfig::openConfig();
0121         KConfigGroup pref = config->group("skrooge_operation");
0122         m_fontFutureOperationsColor = QVariant::fromValue(pref.readEntry("fontFutureColor", QColor(Qt::gray)));
0123         m_fontNotValidatedOperationsColor = QVariant::fromValue(pref.readEntry("fontNotValidatedColor", QColor(Qt::blue)));
0124         m_fontSubOperationsColor = QVariant::fromValue(pref.readEntry("fontSubOperationColor", QColor(Qt::darkGreen)));
0125     }
0126 
0127     if (m_recurrentoperationTable) {
0128         // Read Setting
0129         KSharedConfigPtr config = KSharedConfig::openConfig();
0130         KConfigGroup pref = config->group("skrooge_scheduled");
0131         m_fontDisabledScheduleColor = QVariant::fromValue(pref.readEntry("fontFutureColor", QColor(Qt::gray)));
0132     }
0133 
0134     m_iconFavorite = SKGServices::fromTheme(QStringLiteral("bookmarks"));
0135 
0136     if (m_operationTable || m_recurrentoperationTable) {
0137         m_iconTransfer = SKGServices::fromTheme(QStringLiteral("exchange-positions"));
0138         QStringList overlays;
0139         overlays.push_back(QStringLiteral("list-remove"));
0140         m_iconGroup = SKGServices::fromTheme(QStringLiteral("exchange-positions"), overlays);
0141         m_iconSplit = SKGServices::fromTheme(QStringLiteral("split"));
0142         m_iconImported = SKGServices::fromTheme(QStringLiteral("utilities-file-archiver"));
0143         {
0144             QStringList overlay;
0145             overlay.push_back(QStringLiteral("dialog-ok"));
0146             m_iconImportedChecked = SKGServices::fromTheme(QStringLiteral("utilities-file-archiver"), overlay);
0147         }
0148 
0149         m_iconRecurrent = SKGServices::fromTheme(QStringLiteral("chronometer"));
0150         {
0151             QStringList overlay;
0152             overlay.push_back(QStringLiteral("bookmarks"));
0153             m_iconRecurrentMaster = SKGServices::fromTheme(QStringLiteral("chronometer"), overlay);
0154         }
0155     }
0156 
0157     if (m_budgetTable) {
0158         m_iconGreen = SKGServices::fromTheme(QStringLiteral("security-high"));
0159         m_iconRed = SKGServices::fromTheme(QStringLiteral("security-low"));
0160         m_iconAnber = SKGServices::fromTheme(QStringLiteral("security-medium"));
0161     }
0162 
0163     if (m_unitTable) {
0164         m_iconMuchMore = SKGServices::fromTheme(QStringLiteral("skrooge_much_more"));
0165         m_iconMuchLess = SKGServices::fromTheme(QStringLiteral("skrooge_much_less"));
0166         m_iconMore = SKGServices::fromTheme(QStringLiteral("skrooge_more"));
0167         m_iconLess = SKGServices::fromTheme(QStringLiteral("skrooge_less"));
0168     }
0169 
0170     if (m_ruleTable) {
0171         m_iconSearch = SKGServices::fromTheme(QStringLiteral("edit-find"));
0172         m_iconUpdate = SKGServices::fromTheme(QStringLiteral("view-refresh"));
0173         m_iconAlarm = SKGServices::fromTheme(QStringLiteral("dialog-warning"));
0174         m_iconTemplate = SKGServices::fromTheme(QStringLiteral("edit-guides"));
0175     }
0176     m_iconClosed = SKGServices::fromTheme(QStringLiteral("dialog-close"));
0177 
0178     if (m_categoryTable) {
0179         m_iconCategory = SKGServices::fromTheme(QStringLiteral("view-categories"));
0180         m_iconCategoryMoins = SKGServices::fromTheme(QStringLiteral("view-categories-expenditures"));
0181         m_iconCategoryPlus = SKGServices::fromTheme(QStringLiteral("view-categories-incomes"));
0182     }
0183 }
0184 
0185 SKGObjectModel::~SKGObjectModel()
0186 {
0187     SKGTRACEINFUNC(1)
0188 }
0189 
0190 QVariant SKGObjectModel::headerData(int section, Qt::Orientation orientation, int role) const
0191 {
0192     _SKGTRACEINFUNC(10)
0193 
0194     if (orientation == Qt::Horizontal) {
0195         if (role == Qt::DisplayRole || role == Qt::ToolTipRole) {
0196             QString att;
0197             if (section >= 0 && section < m_listAttibutes.count()) {
0198                 att = m_listAttibutes[section];
0199             } else {
0200                 att = SKGServices::intToString(section);
0201             }
0202 
0203             if (att == QStringLiteral("t_bookmarked") || att == QStringLiteral("i_NBRECURRENT") || att == QStringLiteral("t_status") || att == QStringLiteral("t_close") || att == QStringLiteral("t_imported")) {
0204                 return (role == Qt::ToolTipRole) ? SKGObjectModelBase::headerData(section, orientation, Qt::DisplayRole) : "";
0205             }
0206         }
0207     }
0208     return SKGObjectModelBase::headerData(section, orientation, role);
0209 }
0210 
0211 QString SKGObjectModel::getAttributeForGrouping(const SKGObjectBase& iObject, const QString& iAttribute) const
0212 {
0213     if (m_recurrentoperationTable && iAttribute == QStringLiteral("i_nb_times")) {
0214         if (iObject.getAttribute(QStringLiteral("t_times")) != QStringLiteral("Y")) {
0215             return QChar(8734);
0216         }
0217     } else if (m_ruleTable && iAttribute == QStringLiteral("t_action_type")) {
0218         QString val = iObject.getAttribute(iAttribute);
0219         if (val == QStringLiteral("S")) {
0220             val = i18nc("Noun, a search", "Search");
0221         } else if (val == QStringLiteral("U")) {
0222             val = i18nc("Noun, a modification", "Update");
0223         } else {
0224             val = i18nc("Noun, an alarm", "Alarm");
0225         }
0226         return val;
0227     } else if (iAttribute == QStringLiteral("t_bookmarked") || iAttribute == QStringLiteral("t_close")) {
0228         QString val = iObject.getAttribute(iAttribute);
0229         return val == QStringLiteral("Y") ? i18n("Yes") : i18n("No");
0230     } else if (iAttribute == QStringLiteral("t_status")) {
0231         QString val = iObject.getAttribute(iAttribute);
0232         return val == QStringLiteral("N") ? i18n("None") : val == QStringLiteral("P") ? i18n("Marked") : i18n("Checked");
0233     }
0234     return SKGObjectModelBase::getAttributeForGrouping(iObject, iAttribute);
0235 }
0236 
0237 QVariant SKGObjectModel::computeData(const QModelIndex& iIndex, int iRole) const
0238 {
0239     if (!iIndex.isValid()) {
0240         return QVariant();
0241     }
0242     _SKGTRACEINFUNC(10)
0243     SKGObjectBase* obj = getObjectPointer(iIndex);
0244     if (obj == nullptr || obj->getTable().isEmpty()) {
0245         return SKGObjectModelBase::computeData(iIndex, iRole);
0246     }
0247 
0248     switch (iRole) {
0249     case Qt::DisplayRole:
0250     case Qt::EditRole:
0251     case Qt::UserRole: {
0252         QString att = m_listAttibutes[iIndex.column()];
0253         QString val = obj->getAttribute(att);
0254         if (att == QStringLiteral("i_NBRECURRENT")) {
0255             if (iRole == Qt::UserRole) {
0256                 if (val != QStringLiteral("0")) {
0257                     return QLatin1String("Y");
0258                 }
0259                 if (obj->getAttribute(QStringLiteral("r_recurrentoperation_id")) != QStringLiteral("0")) {
0260                     return QLatin1String("Y");
0261                 }
0262                 return QLatin1String("N");
0263             }
0264             return "";
0265         }
0266         if (att == QStringLiteral("t_bookmarked") ||
0267             att == QStringLiteral("t_status") ||
0268             att == QStringLiteral("t_imported") ||
0269             att == QStringLiteral("t_close") ||
0270             att == QStringLiteral("t_action_type")
0271            ) {
0272             if (iRole == Qt::UserRole) {
0273                 if (m_ruleTable && att == QStringLiteral("t_action_type")) {
0274                     if (val == QStringLiteral("S")) {
0275                         return i18nc("Noun, a search", "Search");
0276                     }
0277                     if (val == QStringLiteral("U")) {
0278                         return i18nc("Noun, a modification", "Update");
0279                     }
0280                     return i18nc("Noun, an alarm", "Alarm");
0281                 }
0282                 return val;
0283             }
0284             return "";
0285         }
0286         if (m_interestTable && att == QStringLiteral("t_expenditure_value_date_mode")) {
0287             if (val == QStringLiteral("0")) {
0288                 return i18nc("Noun", "Day -0");
0289             }
0290             if (val == QStringLiteral("1")) {
0291                 return i18nc("Noun", "Day -1");
0292             }
0293             if (val == QStringLiteral("2")) {
0294                 return i18nc("Noun", "Day -2");
0295             }
0296             if (val == QStringLiteral("3")) {
0297                 return i18nc("Noun", "Day -3");
0298             }
0299             if (val == QStringLiteral("4")) {
0300                 return i18nc("Noun", "Day -4");
0301             }
0302             if (val == QStringLiteral("5")) {
0303                 return i18nc("Noun", "Day -5");
0304             }
0305             return i18nc("Noun", "Fifteen");
0306         }
0307         if (m_accountTable && att == QStringLiteral("d_reconciliationdate")) {
0308             if (val.isEmpty() && iRole == Qt::DisplayRole) {
0309                 return i18nc("Noun", "Never");
0310             }
0311         } else if (m_interestTable && att == QStringLiteral("t_income_value_date_mode")) {
0312             if (val == QStringLiteral("0")) {
0313                 return i18nc("Noun", "Day +0");
0314             }
0315             if (val == QStringLiteral("1")) {
0316                 return i18nc("Noun", "Day +1");
0317             }
0318             if (val == QStringLiteral("2")) {
0319                 return i18nc("Noun", "Day +2");
0320             }
0321             if (val == QStringLiteral("3")) {
0322                 return i18nc("Noun", "Day +3");
0323             }
0324             if (val == QStringLiteral("4")) {
0325                 return i18nc("Noun", "Day +4");
0326             }
0327             if (val == QStringLiteral("5")) {
0328                 return i18nc("Noun", "Day +5");
0329             }
0330             return i18nc("Noun", "Fifteen");
0331         }
0332         if (getAttributeType(iIndex.column()) == SKGServices::FLOAT) {
0333             double dval = SKGServices::stringToDouble(val);
0334             if (iRole == Qt::DisplayRole) {
0335                 if (val.isEmpty()) {
0336                     return "";
0337                 }
0338                 if (att.endsWith(QLatin1String("_INCOME")) ||
0339                     att.endsWith(QLatin1String("_EXPENSE")) ||
0340                     (m_operationTable && obj->getAttribute(QStringLiteral("t_template")) == QStringLiteral("Y")) ||
0341                     (m_categoryTable && (att == QStringLiteral("f_REALCURRENTAMOUNT") ||
0342                                          att == QStringLiteral("f_SUMCURRENTAMOUNT")))) {
0343                     if (dval == 0) {
0344                         return "";
0345                     }
0346                 }
0347 
0348                 SKGServices::SKGUnitInfo unit;
0349                 unit.Symbol = QLatin1String("");
0350                 unit.NbDecimal = 2;
0351                 if (!att.contains(QStringLiteral("QUANTITY")) && !att.contains(QStringLiteral("f_BALANCE_ENTERED"))) {
0352                     unit = qobject_cast<SKGDocumentBank*>(getDocument())->getPrimaryUnit();
0353                     unit.NbDecimal = SKGServices::stringToInt(obj->getAttribute(QStringLiteral("i_NBDEC")));
0354                     if (unit.NbDecimal == 0) {
0355                         unit.NbDecimal = 2;
0356                     }
0357                     if (m_unitvalueTable && !m_cacheUnit.Symbol.isEmpty()) {
0358                         unit = m_cacheUnit;
0359                     }
0360                 } else {
0361                     unit.NbDecimal = SKGServices::stringToInt(obj->getAttribute(QStringLiteral("i_NBDEC")));
0362                     if (unit.NbDecimal == 0) {
0363                         unit.NbDecimal = 2;
0364                     }
0365                     if (att != QStringLiteral("f_QUANTITYOWNED")) {
0366                         unit.Symbol = obj->getAttribute(QStringLiteral("t_UNIT"));
0367                     }
0368                 }
0369 
0370                 // Bug 209672 vvvv
0371                 if (m_unitTable) {
0372                     if (obj->getAttribute(QStringLiteral("t_type")) == QStringLiteral("I")) {
0373                         unit.Symbol = ' ';
0374                     }
0375                 }
0376                 // Bug 209672 ^^^
0377 
0378                 if (QString::compare(att, QStringLiteral("f_rate"), Qt::CaseInsensitive) == 0) {
0379                     unit.Symbol = '%';
0380                     unit.NbDecimal = 2;
0381                 } else if (att == QStringLiteral("f_coef")) {
0382                     unit.Symbol = QLatin1String("");
0383                     unit.NbDecimal = 2;
0384                 }
0385 
0386                 if (unit.Symbol.isEmpty()) {
0387                     unit.Symbol = ' ';
0388                 }
0389 
0390                 return SKGServices::toCurrencyString(dval, unit.Symbol, unit.NbDecimal);
0391             }
0392             return dval;
0393         }
0394         if (getAttributeType(iIndex.column()) == SKGServices::INTEGER) {
0395             if (m_recurrentoperationTable && att == QStringLiteral("i_nb_times")) {
0396                 QString t_times = obj->getAttribute(QStringLiteral("t_times"));
0397                 if (t_times != QStringLiteral("Y")) {
0398                     return QChar(8734);
0399                 }
0400             } else if ((att == QStringLiteral("i_NBOPERATIONS") || att == QStringLiteral("i_SUMNBOPERATIONS")) && val == QStringLiteral("0")) {
0401                 return "";
0402             }
0403 
0404             return SKGServices::stringToInt(val);
0405         }
0406         if (m_suboperationTable && att.startsWith(QLatin1String("p_"))) {
0407             val = obj->getProperty(att.right(att.count() - 2));
0408             if (val.isEmpty()) {
0409                 val = obj->getDocument()->getParameter(att.right(att.count() - 2), obj->getAttribute(QStringLiteral("i_OPID")) % "-operation");
0410             }
0411             return val;
0412         }
0413         if (m_payeeTable && att == QStringLiteral("t_CATEGORY") && val.isEmpty()) {
0414             auto c = qobject_cast<SKGDocumentBank*>(getDocument())->getCategoryForPayee(obj->getAttribute(QStringLiteral("t_name")));
0415             if (!c.isEmpty()) {
0416                 return i18n("Auto: %1", c);
0417             }
0418         }
0419         break;
0420     }
0421     case Qt::DecorationRole: {
0422         // decoration
0423         QString att = m_listAttibutes[iIndex.column()];
0424         if (att == QStringLiteral("t_bookmarked")) {
0425             if (obj->getAttribute(QStringLiteral("t_bookmarked")) == QStringLiteral("Y")) {
0426                 return m_iconFavorite;
0427             }
0428         } else if (m_operationTable || m_recurrentoperationTable) {
0429             if (att == QStringLiteral("t_mode")) {
0430                 if (obj->getAttribute(QStringLiteral("t_TRANSFER")) == QStringLiteral("Y")) {
0431                     return m_iconTransfer;
0432                 }
0433                 if (obj->getAttribute(QStringLiteral("i_group_id")) != QStringLiteral("0")) {
0434                     return m_iconGroup;
0435                 }
0436             } else if (att == QStringLiteral("t_CATEGORY")) {
0437                 if (SKGServices::stringToInt(obj->getAttribute(QStringLiteral("i_NBSUBOPERATIONS"))) > 1) {
0438                     return m_iconSplit;
0439                 }
0440             } else if (att == QStringLiteral("i_NBRECURRENT") && m_operationTable) {
0441                 if (obj->getAttribute(QStringLiteral("i_NBRECURRENT")) != QStringLiteral("0")) {
0442                     return m_iconRecurrentMaster;
0443                 }
0444                 if (obj->getAttribute(QStringLiteral("r_recurrentoperation_id")) != QStringLiteral("0")) {
0445                     return m_iconRecurrent;
0446                 }
0447             } else if (att == QStringLiteral("t_imported")) {
0448                 QString impStatus = obj->getAttribute(QStringLiteral("t_imported"));
0449                 if (impStatus == QStringLiteral("Y")) {
0450                     return m_iconImported;
0451                 }
0452                 if (impStatus == QStringLiteral("P")) {
0453                     return m_iconImportedChecked;
0454                 }
0455             } else if (att == QStringLiteral("t_REFUND") || att == QStringLiteral("t_REALREFUND") || att == QStringLiteral("t_REFUNDDISPLAY")) {
0456                 if (att == QStringLiteral("t_REFUNDDISPLAY")) {
0457                     if (obj->getAttribute(att).count(QStringLiteral("(")) > 1) {
0458                         att.clear();
0459                     } else {
0460                         att = QStringLiteral("t_REFUND");
0461                     }
0462                 }
0463                 if (!att.isEmpty()) {
0464                     QString trackerName;
0465                     trackerName = obj->getAttribute(att);
0466                     if (!trackerName.isEmpty()) {
0467                         SKGTrackerObject tracker(SKGObjectBase(getDocument(), QStringLiteral("refund")));  // Better performance if v_refund is not used
0468                         tracker.setName(trackerName);
0469                         tracker.load();
0470 
0471                         if (tracker.isClosed()) {
0472                             return m_iconClosed;
0473                         }
0474                     }
0475                 }
0476             }
0477         } else if (m_ruleTable) {
0478             if (att == QStringLiteral("t_action_type")) {
0479                 QString val = obj->getAttribute(att);
0480                 if (val == QStringLiteral("S")) {
0481                     return m_iconSearch;
0482                 }
0483                 if (val == QStringLiteral("U")) {
0484                     return m_iconUpdate;
0485                 }
0486                 if (val == QStringLiteral("T")) {
0487                     return m_iconTemplate;
0488                 }
0489                 return m_iconAlarm;
0490             }
0491         } else if (m_unitTable) {
0492             if (att == QStringLiteral("f_CURRENTAMOUNT")) {
0493                 SKGUnitObject unit(*obj);
0494                 double amountOneYearBefore = unit.getAmount(QDate::currentDate().addYears(-1));
0495                 double annualchange = 100.0 * (unit.getAmount() - amountOneYearBefore) / amountOneYearBefore;
0496                 if (annualchange >= 10) {
0497                     return m_iconMuchMore;
0498                 }
0499                 if (annualchange <= -10) {
0500                     return m_iconMuchLess;
0501                 }
0502                 if (annualchange > 0) {
0503                     return m_iconMore;
0504                 }
0505                 if (annualchange < 0) {
0506                     return m_iconLess;
0507                 }
0508             }
0509         } else if (m_accountTable) {
0510             SKGAccountObject act(*obj);
0511             if (att == QStringLiteral("t_BANK")) {
0512                 const bool wallet = (act.getType() == SKGAccountObject::WALLET);
0513                 QString t_icon = wallet ? QStringLiteral("wallet-closed") : obj->getAttribute(QStringLiteral("t_icon"));
0514                 if (t_icon.isEmpty()) {
0515                     t_icon = obj->getAttribute(QStringLiteral("t_ICON"));
0516                 }
0517                 if (!t_icon.isEmpty()) {
0518                     QString fileName = QStandardPaths::locate(QStandardPaths::GenericDataLocation, "skrooge/images/logo/" % t_icon);
0519                     if (fileName.isEmpty()) {
0520                         fileName = t_icon;
0521                     }
0522                     return QVariant::fromValue(SKGServices::fromTheme(fileName));
0523                 }
0524             } else if (att == QStringLiteral("f_importbalance")) {
0525                 QString val = obj->getAttribute(att);
0526                 if (val.isEmpty()) {
0527                     return "";
0528                 }
0529 
0530                 // Compute value
0531                 auto soluces = act.getPossibleReconciliations(SKGServices::stringToDouble(val), false);
0532                 return SKGServices::fromTheme(soluces.isEmpty() ? QStringLiteral("security-low") : QStringLiteral("security-high"));
0533             } else if (att == QStringLiteral("f_reconciliationbalance")) {
0534                 QString val = obj->getAttribute(att);
0535                 if (val.isEmpty()) {
0536                     return "";
0537                 }
0538 
0539                 // Compute value
0540                 auto soluces = act.getPossibleReconciliations(SKGServices::stringToDouble(val), false);
0541                 return SKGServices::fromTheme(soluces.isEmpty() ? QStringLiteral("security-low") : QStringLiteral("security-high"));
0542             }
0543         } else if (m_categoryTable) {
0544             if (iIndex.column() == 0) {
0545                 QString t_TYPEEXPENSE = obj->getAttribute(QStringLiteral("t_TYPEEXPENSE"));
0546                 if (t_TYPEEXPENSE == QStringLiteral("-")) {
0547                     return m_iconCategoryMoins;
0548                 }
0549                 if (t_TYPEEXPENSE == QStringLiteral("+")) {
0550                     return m_iconCategoryPlus;
0551                 }
0552                 return m_iconCategory;
0553             }
0554         } else if (m_budgetTable) {
0555             if (att == QStringLiteral("f_DELTA") || att == QStringLiteral("f_DELTABEFORETRANSFER")) {
0556                 double val = SKGServices::stringToDouble(obj->getAttribute(att));
0557                 if (val < -EPSILON) {
0558                     return m_iconRed;
0559                 }
0560                 if (val > EPSILON) {
0561                     return m_iconGreen;
0562                 }
0563                 double transferred = SKGServices::stringToDouble(obj->getAttribute(QStringLiteral("f_transferred")));
0564                 if (transferred < -EPSILON) {
0565                     return m_iconAnber;
0566                 }
0567                 return m_iconGreen;
0568             }
0569         }
0570         break;
0571     }
0572     case Qt::TextColorRole: {
0573         // Text color
0574         QString att = m_listAttibutes[iIndex.column()];
0575         if (m_recurrentoperationTable) {
0576             if (obj->getAttribute(QStringLiteral("i_nb_times")) == QStringLiteral("0")) {
0577                 return  m_fontDisabledScheduleColor;
0578             }
0579             if (att == QStringLiteral("d_date") && SKGServices::stringToTime(obj->getAttribute(QStringLiteral("d_date"))).date() <= QDate::currentDate()) {
0580                 return m_fontNegativeColor;
0581             }
0582         } else if (m_operationTable) {
0583             if (SKGServices::stringToTime(obj->getAttribute(QStringLiteral("d_date"))).date() > QDate::currentDate()) {
0584                 return m_fontFutureOperationsColor;
0585             }
0586             if (getAttributeType(iIndex.column()) != SKGServices::FLOAT) {
0587                 if (obj->getAttribute(QStringLiteral("t_imported")) == QStringLiteral("P")) {
0588                     return  m_fontNotValidatedOperationsColor;
0589                 }
0590                 if (m_suboperationTable) {
0591                     return m_fontSubOperationsColor;
0592                 }
0593             }
0594         }
0595         break;
0596     }
0597     case Qt::TextAlignmentRole: {
0598         // Text alignment
0599         if (m_recurrentoperationTable) {
0600             QString att = m_listAttibutes[iIndex.column()];
0601 
0602             if (att == QStringLiteral("i_auto_write_days") || att == QStringLiteral("i_warn_days") || att == QStringLiteral("i_nb_times")) {
0603                 return static_cast<int>(Qt::AlignVCenter | Qt::AlignLeft);
0604             }
0605         }
0606         break;
0607     }
0608     case Qt::CheckStateRole: {
0609         // CheckState
0610         QString att = m_listAttibutes[iIndex.column()];
0611         if (m_operationTable && att == QStringLiteral("t_status")) {
0612             QString cond = obj->getAttribute(QStringLiteral("t_status"));
0613             if (cond == QStringLiteral("P")) {
0614                 return static_cast<int>(Qt::PartiallyChecked);
0615             }
0616             if (cond == QStringLiteral("Y")) {
0617                 return static_cast<int>(Qt::Checked);
0618             }
0619             if (cond == QStringLiteral("N")) {
0620                 return static_cast<int>(Qt::Unchecked);
0621             }
0622         } else if (att == QStringLiteral("t_close")) {
0623             QString cond = obj->getAttribute(QStringLiteral("t_close"));
0624             if (cond == QStringLiteral("Y")) {
0625                 return static_cast<int>(Qt::Checked);
0626             }
0627             return static_cast<int>(Qt::Unchecked);
0628 
0629         } else  if (m_recurrentoperationTable && att == QStringLiteral("i_auto_write_days")) {
0630             QString cond = obj->getAttribute(QStringLiteral("t_auto_write"));
0631             if (cond == QStringLiteral("Y")) {
0632                 return static_cast<int>(Qt::Checked);
0633             }
0634             return static_cast<int>(Qt::Unchecked);
0635 
0636         } else  if (m_recurrentoperationTable && att == QStringLiteral("i_warn_days")) {
0637             QString cond = obj->getAttribute(QStringLiteral("t_warn"));
0638             if (cond == QStringLiteral("Y")) {
0639                 return static_cast<int>(Qt::Checked);
0640             }
0641             return static_cast<int>(Qt::Unchecked);
0642 
0643         } else  if (m_recurrentoperationTable && att == QStringLiteral("i_nb_times")) {
0644             QString cond = obj->getAttribute(QStringLiteral("t_times"));
0645             if (cond == QStringLiteral("Y")) {
0646                 return static_cast<int>(Qt::Checked);
0647             }
0648             return static_cast<int>(Qt::Unchecked);
0649 
0650         } else  if (m_budgetRuleTable && att == QStringLiteral("t_CATEGORYCONDITION")) {
0651             QString cond = obj->getAttribute(QStringLiteral("t_category_condition"));
0652             if (cond == QStringLiteral("Y")) {
0653                 return static_cast<int>(Qt::Checked);
0654             }
0655             return static_cast<int>(Qt::Unchecked);
0656 
0657         } else  if (m_budgetRuleTable && att == QStringLiteral("i_year")) {
0658             QString cond = obj->getAttribute(QStringLiteral("t_year_condition"));
0659             if (cond == QStringLiteral("Y")) {
0660                 return static_cast<int>(Qt::Checked);
0661             }
0662             return static_cast<int>(Qt::Unchecked);
0663 
0664         } else  if (m_budgetRuleTable && att == QStringLiteral("i_month")) {
0665             QString cond = obj->getAttribute(QStringLiteral("t_month_condition"));
0666             if (cond == QStringLiteral("Y")) {
0667                 return static_cast<int>(Qt::Checked);
0668             }
0669             return static_cast<int>(Qt::Unchecked);
0670 
0671         } else  if (m_budgetRuleTable && att == QStringLiteral("t_CATEGORY")) {
0672             QString cond = obj->getAttribute(QStringLiteral("t_category_target"));
0673             if (cond == QStringLiteral("Y")) {
0674                 return static_cast<int>(Qt::Checked);
0675             }
0676             return static_cast<int>(Qt::Unchecked);
0677 
0678         } else  if (m_budgetTable && att == QStringLiteral("t_CATEGORY")) {
0679             QString cond = obj->getAttribute(QStringLiteral("t_including_subcategories"));
0680             if (cond == QStringLiteral("Y")) {
0681                 return static_cast<int>(Qt::Checked);
0682             }
0683             return static_cast<int>(Qt::Unchecked);
0684         }
0685         break;
0686     }
0687     case Qt::ToolTipRole: {
0688         // Tooltip
0689         QString toolTipString;
0690         QString att = m_listAttibutes[iIndex.column()];
0691         if (getAttributeType(iIndex.column()) == SKGServices::FLOAT) {
0692             // Add secondary unit
0693             if (!att.contains(QStringLiteral("QUANTITY")) && att != QStringLiteral("f_coef") && att != QStringLiteral("f_rate")) {
0694                 SKGServices::SKGUnitInfo secondaryUnit = qobject_cast<SKGDocumentBank*>(getDocument())->getSecondaryUnit();
0695                 if (!secondaryUnit.Symbol.isEmpty()) {
0696                     double val = SKGServices::stringToDouble(obj->getAttribute(att));
0697                     if ((!att.endsWith(QLatin1String("_INCOME")) && !att.endsWith(QLatin1String("_EXPENSE"))) || val != 0) {
0698                         if (secondaryUnit.Value != 0.0) {
0699                             toolTipString = SKGServices::toCurrencyString(val / secondaryUnit.Value, secondaryUnit.Symbol, secondaryUnit.NbDecimal);
0700                         }
0701                     }
0702                 }
0703             }
0704 
0705             // Add balance
0706             if (m_operationTable && !m_suboperationTable) {
0707                 SKGOperationObject op(*obj);
0708                 if (!op.isTemplate()) {
0709                     SKGServices::SKGUnitInfo primaryUnit = qobject_cast<SKGDocumentBank*>(getDocument())->getPrimaryUnit();
0710 
0711                     // Add original amount
0712                     QString originalAmount = obj->getProperty(QStringLiteral("SKG_OP_ORIGINAL_AMOUNT"));
0713                     if (!originalAmount.isEmpty()) {
0714                         if (!toolTipString.isEmpty()) {
0715                             toolTipString += QStringLiteral("\n\n");
0716                         }
0717                         double val1 = SKGServices::stringToDouble(obj->getAttribute(QStringLiteral("f_CURRENTAMOUNT")));
0718                         double val2 = SKGServices::stringToDouble(originalAmount);
0719                         double gain = (val2 != 0 ? 100.0 * (val1 - val2) / val2 : 0);
0720                         double gainperyear = gain;
0721 
0722                         int nbDays = op.getDate().daysTo(QDate::currentDate());
0723                         if (nbDays != 0 && val2 != 0) {
0724                             double gainperday = 100.0 * expm1(log(val1 / val2) / static_cast<double>(nbDays));
0725                             gainperyear = 100.0 * (pow(1.0 + gainperday / 100.0, 365.0) - 1);
0726                         }
0727                         toolTipString += i18nc("Noun", "Original amount=%1 (%2 = %3 / year)",
0728                                                SKGServices::toCurrencyString(val2, primaryUnit.Symbol, primaryUnit.NbDecimal),
0729                                                (gain >= 0 ? "+" : "-") % SKGServices::toPercentageString(gain, 2),
0730                                                (gainperyear >= 0 ? "+" : "-") % SKGServices::toPercentageString(gainperyear, 2));
0731                     } else {
0732                         if (!toolTipString.isEmpty()) {
0733                             toolTipString += QStringLiteral("\n\n");
0734                         }
0735                         double val1 = SKGServices::stringToDouble(obj->getAttribute(QStringLiteral("f_CURRENTAMOUNT")));
0736                         double val2 = op.getAmount(op.getDate());
0737                         double gain = (val2 != 0 ? 100.0 * (val1 - val2) / val2 : 0);
0738                         double gainperyear = gain;
0739 
0740                         int nbDays = op.getDate().daysTo(QDate::currentDate());
0741                         if (nbDays != 0 && val2 != 0) {
0742                             double gainperday = 100.0 * expm1(log(val1 / val2) / static_cast<double>(nbDays));
0743                             gainperyear = 100.0 * (pow(1.0 + gainperday / 100.0, 365.0) - 1);
0744                         }
0745 
0746                         QString sval1 = SKGServices::toCurrencyString(val1, primaryUnit.Symbol, primaryUnit.NbDecimal);
0747                         QString sval2 = SKGServices::toCurrencyString(val2, primaryUnit.Symbol, primaryUnit.NbDecimal);
0748                         if (sval1 != sval2) {
0749                             toolTipString += i18nc("Noun", "Amount at creation date=%1 (%2 = %3 / year)",
0750                                                    sval2,
0751                                                    (gain >= 0 ? "+" : "-") % SKGServices::toPercentageString(gain, 2),
0752                                                    (gainperyear >= 0 ? "+" : "-") % SKGServices::toPercentageString(gainperyear, 2));
0753 
0754                             toolTipString += '\n';
0755                         }
0756                     }
0757                     toolTipString += i18nc("Noun", "Account balance=%1",
0758                                            SKGServices::toCurrencyString(op.getBalance(), primaryUnit.Symbol, primaryUnit.NbDecimal));
0759                 }
0760             }
0761 
0762             if (m_budgetTable) {
0763                 if (att == QStringLiteral("f_DELTA")) {
0764                     double val = SKGServices::stringToDouble(obj->getAttribute(QStringLiteral("f_DELTABEFORETRANSFER")));
0765 
0766                     if (!toolTipString.isEmpty()) {
0767                         toolTipString += QStringLiteral("\n\n");
0768                     }
0769 
0770                     SKGServices::SKGUnitInfo primaryUnit = qobject_cast<SKGDocumentBank*>(getDocument())->getPrimaryUnit();
0771                     toolTipString += i18nc("Noun", "Original delta=%1",
0772                                            SKGServices::toCurrencyString(val, primaryUnit.Symbol, primaryUnit.NbDecimal));
0773                 } else if (att == QStringLiteral("f_budgeted_modified")) {
0774                     QString reasons = obj->getAttribute(QStringLiteral("t_modification_reasons"));
0775                     if (!reasons.isEmpty()) {
0776                         if (!toolTipString.isEmpty()) {
0777                             toolTipString += QStringLiteral("\n\n");
0778                         }
0779                         toolTipString += reasons;
0780                     }
0781                 }
0782             }
0783 
0784             if (m_unitTable) {
0785                 if (att == QStringLiteral("f_CURRENTAMOUNT")) {
0786                     SKGUnitObject unit(*obj);
0787                     double amountOneYearBefore = unit.getAmount(QDate::currentDate().addYears(-1));
0788                     double annualchange = 100.0 * (unit.getAmount() - amountOneYearBefore) / amountOneYearBefore;
0789 
0790                     double amount6MonthBefore = unit.getAmount(QDate::currentDate().addMonths(-6));
0791                     double sixMonthChange = 100.0 * (unit.getAmount() - amount6MonthBefore) / amount6MonthBefore;
0792 
0793                     double amount1MonthBefore = unit.getAmount(QDate::currentDate().addMonths(-1));
0794                     double oneMonthChange = 100.0 * (unit.getAmount() - amount1MonthBefore) / amount1MonthBefore;
0795                     if (!toolTipString.isEmpty()) {
0796                         toolTipString += QStringLiteral("\n\n");
0797                     }
0798                     toolTipString += i18nc("Noun", "Variation on the last year = %1\n", SKGServices::toPercentageString(annualchange));
0799                     toolTipString += i18nc("Noun", "Variation on the last 6 months = %1 = %2 / year\n", SKGServices::toPercentageString(sixMonthChange), SKGServices::toPercentageString(100.0 * pow(1.0 + sixMonthChange / 100.0, 2.0) - 100.0));
0800                     toolTipString += i18nc("Noun", "Variation on the last month = %1 = %2 / year\n", SKGServices::toPercentageString(oneMonthChange), SKGServices::toPercentageString(100.0 * pow(1.0 + oneMonthChange / 100.0, 12.0) - 100.0));
0801                 }
0802             }
0803         } else if (m_operationTable || m_recurrentoperationTable) {
0804             if (att == QStringLiteral("t_imported")) {
0805                 if (!m_suboperationTable) {
0806                     SKGOperationObject op;
0807                     if (m_recurrentoperationTable) {
0808                         SKGRecurrentOperationObject rop(*obj);
0809                         rop.getParentOperation(op);
0810                     } else {
0811                         op = *obj;
0812                     }
0813                     toolTipString = op.getImportID();
0814                 }
0815             } else if (att == QStringLiteral("t_REFUND") || att == QStringLiteral("t_REALREFUND") || att == QStringLiteral("t_REFUNDDISPLAY")) {
0816                 if (att == QStringLiteral("t_REFUNDDISPLAY")) {
0817                     if (obj->getAttribute(att).count(QStringLiteral("(")) > 1) {
0818                         att.clear();
0819                     } else {
0820                         att = QStringLiteral("t_REFUND");
0821                     }
0822                 }
0823                 if (!att.isEmpty()) {
0824                     QString trackerName = obj->getAttribute(att);
0825                     if (!trackerName.isEmpty()) {
0826                         SKGTrackerObject tracker(getDocument());
0827                         tracker.setName(trackerName);
0828                         tracker.load();
0829                         SKGServices::SKGUnitInfo unit = qobject_cast<SKGDocumentBank*>(getDocument())->getPrimaryUnit();
0830                         toolTipString = SKGServices::toCurrencyString(tracker.getCurrentAmount(), unit.Symbol, unit.NbDecimal);
0831                     }
0832                 }
0833             } else if (att == QStringLiteral("t_PAYEE")) {
0834                 QString payeeName = obj->getAttribute(att);
0835                 if (!payeeName.isEmpty()) {
0836                     SKGPayeeObject payee(getDocument());
0837                     payee.setName(payeeName);
0838                     payee.load();
0839 
0840                     auto address = payee.getAddress();
0841                     if (!address.isEmpty()) {
0842                         toolTipString += i18nc("Information", "Address= %1\n", address);
0843                     }
0844 
0845                     auto c = payee.getAttribute(QStringLiteral("t_CATEGORY"));
0846                     if (c.isEmpty()) {
0847                         c = qobject_cast<SKGDocumentBank*>(getDocument())->getCategoryForPayee(payeeName, false);
0848                     }
0849                     if (!c.isEmpty()) {
0850                         toolTipString += i18nc("Information", "Category= %1\n", c);
0851                     }
0852                 }
0853             }  else if (att == QStringLiteral("t_ACCOUNT")  || att == QStringLiteral("t_TOACCOUNT")) {
0854                 QString accountName = obj->getAttribute(att);
0855                 if (!accountName.isEmpty()) {
0856                     SKGAccountObject account(getDocument());
0857                     account.setName(accountName);
0858                     account.load();
0859 
0860                     auto tmp = account.getAgencyNumber();
0861                     if (!tmp.isEmpty()) {
0862                         toolTipString += i18nc("Information", "Agency number= %1\n", tmp);
0863                     }
0864 
0865                     tmp = account.getNumber();
0866                     if (!tmp.isEmpty()) {
0867                         toolTipString += i18nc("Information", "Number= %1\n", tmp);
0868                     }
0869 
0870                     tmp = account.getAgencyAddress();
0871                     if (!tmp.isEmpty()) {
0872                         toolTipString += i18nc("Information", "Address= %1\n", tmp);
0873                     }
0874 
0875                     tmp = account.getComment();
0876                     if (!tmp.isEmpty()) {
0877                         toolTipString += i18nc("Information", "Comment= %1\n", tmp);
0878                     }
0879                 }
0880             } else if (att == QStringLiteral("t_CATEGORY")) {
0881                 SKGOperationObject op(*obj);
0882                 if (m_recurrentoperationTable) {
0883                     SKGRecurrentOperationObject rop(*obj);
0884                     rop.getParentOperation(op);
0885                 }
0886                 if (SKGServices::stringToInt(op.getAttribute(QStringLiteral("i_NBSUBOPERATIONS"))) > 1) {
0887                     SKGObjectBase::SKGListSKGObjectBase subOps;
0888                     op.getSubOperations(subOps);
0889                     for (const auto& subOp : qAsConst(subOps)) {
0890                         toolTipString += subOp.getDisplayName() % '\n';
0891                     }
0892                 }
0893             } else if (att == QStringLiteral("t_mode")) {
0894                 SKGOperationObject op(*obj);
0895                 if (m_recurrentoperationTable) {
0896                     SKGRecurrentOperationObject rop(*obj);
0897                     rop.getParentOperation(op);
0898                 }
0899                 if (op.getAttribute(QStringLiteral("i_group_id")) != QStringLiteral("0")) {
0900                     SKGOperationObject gop;
0901                     op.getGroupOperation(gop);
0902 
0903                     SKGObjectBase::SKGListSKGObjectBase gOps;
0904                     op.getGroupedOperations(gOps);
0905                     for (const auto& item : qAsConst(gOps)) {
0906                         SKGOperationObject gOp(item);
0907                         SKGAccountObject account;
0908                         gOp.getParentAccount(account);
0909                         toolTipString += account.getDisplayName() % '\n';
0910                     }
0911                 }
0912             }
0913 
0914             if (m_operationTable && !m_suboperationTable && toolTipString.isEmpty()) {
0915                 SKGOperationObject op(*obj);
0916                 if (op.getStatus() == SKGOperationObject::MARKED) {
0917                     toolTipString = i18nc("A tool tip", "This transaction is marked but not checked yet.");
0918                     toolTipString += '\n';
0919                     toolTipString += i18nc("A tool tip", "You can use the reconciliation mode to validate marked transactions.");
0920                     toolTipString += '\n';
0921                     if (att == QStringLiteral("t_status")) {
0922                         toolTipString += i18nc("A tool tip", "Click in this column to switch back status.");
0923                     } else {
0924                         toolTipString += i18nc("A tool tip", "Click in the Status (checkmark) column to switch back status.");
0925                     }
0926                     toolTipString += '\n';
0927                     toolTipString += i18nc("A tool tip", "Ctrl+click to force checked status.");
0928                 } else if (op.getStatus() == SKGOperationObject::CHECKED) {
0929                     toolTipString = i18nc("A tool tip", "This transaction is already checked.");
0930                 } else if (op.getStatus() == SKGOperationObject::NONE) {
0931                     toolTipString = i18nc("A tool tip", "This transaction is not marked yet.");
0932                     toolTipString += '\n';
0933                     toolTipString += i18nc("A tool tip", "Click to set marked status.");
0934                     toolTipString += '\n';
0935                     toolTipString += i18nc("A tool tip", "Ctrl+click to force checked status.");
0936                 }
0937             }
0938         } else if (m_ruleTable && att == QStringLiteral("t_action_type")) {
0939             QString val = obj->getAttribute(att);
0940             if (val == QStringLiteral("S")) {
0941                 toolTipString = i18nc("Noun, a search", "Search");
0942             } else if (val == QStringLiteral("U")) {
0943                 toolTipString = i18nc("Noun, a modification", "Update");
0944             } else {
0945                 toolTipString = i18nc("Noun, an alarm", "Alarm");
0946             }
0947         }
0948 
0949         QString toolTipStringBase = SKGObjectModelBase::computeData(iIndex, iRole).toString();
0950         if (!toolTipStringBase.isEmpty()) {
0951             if (!toolTipString.isEmpty()) {
0952                 toolTipString += QStringLiteral("\n\n");
0953             }
0954             toolTipString += toolTipStringBase;
0955         }
0956         return toolTipString;
0957     }
0958     default: {
0959     }
0960     }
0961     return SKGObjectModelBase::computeData(iIndex, iRole);
0962 }
0963 
0964 bool SKGObjectModel::setData(const QModelIndex& iIndex, const QVariant& iValue, int iRole)
0965 {
0966     if (!iIndex.isValid()) {
0967         return false;
0968     }
0969 
0970     if (iRole == Qt::CheckStateRole) {
0971         SKGError err;
0972         {
0973             auto newState = static_cast<Qt::CheckState>(iValue.toInt());
0974             if (m_accountTable) {
0975                 SKGAccountObject obj(getObject(iIndex));
0976                 SKGBEGINLIGHTTRANSACTION(*getDocument(),
0977                                          (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);
0978                 if (qAbs(obj.getCurrentAmount()) > 0.01 && newState == Qt::Checked) {
0979                     err = getDocument()->sendMessage(i18nc("An information message",  "Warning, you closed an account with money"), SKGDocument::Warning);
0980                 }
0981                 IFOKDO(err, obj.setClosed(newState == Qt::Checked))
0982                 IFOKDO(err, obj.save())
0983             } else if (m_trackerTable) {
0984                 SKGTrackerObject obj(getObject(iIndex));
0985                 SKGBEGINLIGHTTRANSACTION(*getDocument(),
0986                                          (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);
0987                 err = obj.setClosed(newState == Qt::Checked);
0988                 IFOKDO(err, obj.save())
0989             }  else if (m_categoryTable) {
0990                 SKGCategoryObject obj(getObject(iIndex));
0991                 SKGBEGINLIGHTTRANSACTION(*getDocument(),
0992                                          (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);
0993                 err = obj.setClosed(newState == Qt::Checked);
0994                 IFOKDO(err, obj.save())
0995             }  else if (m_payeeTable) {
0996                 SKGPayeeObject obj(getObject(iIndex));
0997                 SKGBEGINLIGHTTRANSACTION(*getDocument(),
0998                                          (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);
0999                 err = obj.setClosed(newState == Qt::Checked);
1000                 IFOKDO(err, obj.save())
1001             } else if (m_operationTable && !m_suboperationTable) {
1002                 // Get the real object, not the object from the view
1003                 SKGObjectBase* objtmp = getObjectPointer(iIndex);
1004                 if (objtmp != nullptr) {
1005                     SKGOperationObject obj = SKGOperationObject(objtmp->getDocument(), objtmp->getID());
1006                     SKGBEGINLIGHTTRANSACTION(*getDocument(), i18nc("Noun, name of the user action", "Change transaction status"), err)
1007                     SKGOperationObject::OperationStatus statusinitial = obj.getStatus();
1008                     SKGOperationObject::OperationStatus t_status = statusinitial;
1009                     if ((QApplication::keyboardModifiers() & Qt::ControlModifier) != 0u) {
1010                         // 2747379: NONE ==> CHECKED, MARKED ==> CHECKED, CHECKED ==> CHECKED
1011                         t_status = SKGOperationObject::CHECKED;
1012                         // t_status= ( t_status==SKGOperationObject::MARKED ? SKGOperationObject::NONE : ( t_status==SKGOperationObject::CHECKED ? SKGOperationObject::MARKED : SKGOperationObject::NONE ) );
1013                     } else {
1014                         // 2747379: NONE ==> MARKED, MARKED ==> NONE, CHECKED ==> MARKED
1015                         t_status = (t_status == SKGOperationObject::NONE ? SKGOperationObject::MARKED : (t_status == SKGOperationObject::MARKED ? SKGOperationObject::NONE : SKGOperationObject::MARKED));
1016                         // t_status=(t_status==SKGOperationObject::MARKED ? SKGOperationObject::CHECKED : (t_status==SKGOperationObject::CHECKED ? SKGOperationObject::CHECKED : SKGOperationObject::MARKED ));
1017                     }
1018                     if (t_status != statusinitial) {
1019                         err = obj.setStatus(t_status);
1020                         IFOKDO(err, obj.save())
1021 
1022                         // Send message
1023                         IFOKDO(err, getDocument()->sendMessage(i18nc("An information to the user", "The status of the transaction '%1' has been changed", obj.getDisplayName()), SKGDocument::Hidden))
1024                     }
1025                 }
1026             } else if (m_recurrentoperationTable) {
1027                 QString att = m_listAttibutes[iIndex.column()];
1028 
1029                 // Get the real object, not the object from the view
1030                 SKGObjectBase* objtmp = getObjectPointer(iIndex);
1031                 if (objtmp != nullptr) {
1032                     SKGRecurrentOperationObject obj = SKGRecurrentOperationObject(objtmp->getDocument(), objtmp->getID());
1033 
1034                     SKGBEGINLIGHTTRANSACTION(*getDocument(), i18nc("Noun, name of the user action", "Recurrent transaction update"), err)
1035                     if (att == QStringLiteral("i_warn_days")) {
1036                         err = obj.warnEnabled(!obj.isWarnEnabled());
1037                     } else if (att == QStringLiteral("i_auto_write_days")) {
1038                         err = obj.autoWriteEnabled(!obj.isAutoWriteEnabled());
1039                     } else if (att == QStringLiteral("i_nb_times")) {
1040                         err = obj.timeLimit(!obj.hasTimeLimit());
1041                     }
1042                     IFOKDO(err, obj.save())
1043 
1044                     // Send message
1045                     IFOKDO(err, getDocument()->sendMessage(i18nc("An information to the user", "The recurrent transaction '%1' has been updated", obj.getDisplayName()), SKGDocument::Hidden))
1046                 }
1047             } else if (m_budgetRuleTable) {
1048                 QString att = m_listAttibutes[iIndex.column()];
1049 
1050                 // Get the real object, not the object from the view
1051                 SKGObjectBase* objtmp = getObjectPointer(iIndex);
1052                 if (objtmp != nullptr) {
1053                     SKGBudgetRuleObject obj = SKGBudgetRuleObject(objtmp->getDocument(), objtmp->getID());
1054 
1055                     SKGBEGINLIGHTTRANSACTION(*getDocument(), i18nc("Noun, name of the user action", "Budget rule update"), err)
1056                     if (att == QStringLiteral("i_year")) {
1057                         err = obj.enableYearCondition(!obj.isYearConditionEnabled());
1058                     } else if (att == QStringLiteral("i_month")) {
1059                         err = obj.enableMonthCondition(!obj.isMonthConditionEnabled());
1060                     } else if (att == QStringLiteral("t_CATEGORYCONDITION")) {
1061                         err = obj.enableCategoryCondition(!obj.isCategoryConditionEnabled());
1062                     } else if (att == QStringLiteral("t_CATEGORY")) {
1063                         err = obj.enableCategoryChange(!obj.isCategoryChangeEnabled());
1064                     }
1065                     IFOKDO(err, obj.save())
1066 
1067                     // Send message
1068                     IFOKDO(err, getDocument()->sendMessage(i18nc("An information to the user", "The budget rule '%1' has been updated", obj.getDisplayName()), SKGDocument::Hidden))
1069                 }
1070             } else if (m_budgetTable) {
1071                 QString att = m_listAttibutes[iIndex.column()];
1072 
1073                 // Get the real object, not the object from the view
1074                 SKGObjectBase* objtmp = getObjectPointer(iIndex);
1075                 if (objtmp != nullptr) {
1076                     SKGBudgetObject obj = SKGBudgetObject(objtmp->getDocument(), objtmp->getID());
1077 
1078                     SKGBEGINTRANSACTION(*getDocument(), i18nc("Noun, name of the user action", "Budget update"), err)
1079                     if (att == QStringLiteral("t_CATEGORY")) {
1080                         IFOKDO(err, obj.enableSubCategoriesInclusion(!obj.isSubCategoriesInclusionEnabled()))
1081                         IFOKDO(err, obj.save())
1082 
1083                         // Send message
1084                         IFOKDO(err, getDocument()->sendMessage(i18nc("An information to the user", "The budget '%1' have been updated", obj.getDisplayName()), SKGDocument::Hidden))
1085                     }
1086                 }
1087             }
1088         }
1089 
1090         SKGMainPanel::displayErrorMessage(err);
1091         return !err;
1092     }
1093     return SKGObjectModelBase::setData(iIndex, iValue, iRole);
1094 }
1095 
1096 Qt::ItemFlags SKGObjectModel::flags(const QModelIndex& iIndex) const
1097 {
1098     _SKGTRACEINFUNC(10)
1099 
1100     Qt::ItemFlags f = SKGObjectModelBase::flags(iIndex);
1101 
1102     if (iIndex.isValid()) {
1103         QString att = m_listAttibutes[iIndex.column()];
1104         if (att == QStringLiteral("t_bookmarked") || m_ruleTable || m_recurrentoperationTable || m_interestTable || m_interestResultTable) {
1105             f = f & ~Qt::ItemIsEditable;
1106         }
1107     }
1108 
1109     if (m_categoryTable || m_payeeTable || m_accountTable || m_unitTable || m_trackerTable) {
1110         if (iIndex.isValid()) {
1111             f |= Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled;
1112         } else {
1113             f |= Qt::ItemIsDropEnabled;
1114         }
1115     }
1116 
1117     return f;
1118 }
1119 
1120 Qt::DropActions SKGObjectModel::supportedDragActions() const
1121 {
1122     if (m_categoryTable || m_payeeTable || m_accountTable || m_unitTable || m_trackerTable) {
1123         return Qt::MoveAction;
1124     }
1125     return SKGObjectModelBase::supportedDragActions();
1126 }
1127 
1128 Qt::DropActions SKGObjectModel::supportedDropActions() const
1129 {
1130     return SKGObjectModelBase::supportedDropActions();
1131 }
1132 
1133 bool SKGObjectModel::dropMimeData(const QMimeData* iData,
1134                                   Qt::DropAction iAction,
1135                                   int iRow, int iColumn,
1136                                   const QModelIndex& iParent)
1137 {
1138     if (SKGObjectModelBase::dropMimeData(iData, iAction, iRow, iColumn, iParent)) {
1139         return true;
1140     }
1141     if (iAction == Qt::IgnoreAction) {
1142         return true;
1143     }
1144     if ((iData == nullptr) || !(iData->hasFormat(QStringLiteral("application/skg.category.ids")) ||
1145                                 iData->hasFormat(QStringLiteral("application/skg.payee.ids")) ||
1146                                 iData->hasFormat(QStringLiteral("application/skg.account.ids")) ||
1147                                 iData->hasFormat(QStringLiteral("application/skg.refund.ids")) ||
1148                                 iData->hasFormat(QStringLiteral("application/skg.unit.ids")))) {
1149         return false;
1150     }
1151     if (iColumn > 0) {
1152         return false;
1153     }
1154 
1155     SKGError err;
1156     if (iData->hasFormat(QStringLiteral("application/skg.category.ids"))) {
1157         QByteArray encodedData = iData->data(QStringLiteral("application/skg.category.ids"));
1158         QDataStream stream(&encodedData, QIODevice::ReadOnly);
1159         QStringList newItems;
1160 
1161         SKGCategoryObject parentCategory;
1162         if (iParent.isValid()) {
1163             parentCategory = getObject(iParent);
1164         }
1165         {
1166             SKGBEGINTRANSACTION(*getDocument(), i18nc("Noun, name of the user action", "Move category"), err)
1167 
1168             while (!stream.atEnd() && !err) {
1169                 int o_id;
1170                 QString o_table;
1171                 stream >> o_table;
1172                 stream >> o_id;
1173 
1174                 SKGCategoryObject child(getDocument(), o_id);
1175                 err = child.load();
1176                 QString oldName = child.getDisplayName();
1177                 IFOK(err) {
1178                     if (iParent.isValid()) {
1179                         err = child.setParentCategory(parentCategory);
1180                     } else {
1181                         err = child.removeParentCategory();
1182                     }
1183                 }
1184                 IFOKDO(err, child.save())
1185 
1186                 // Send message
1187                 IFOKDO(err, getDocument()->sendMessage(i18nc("An information to the user", "The category '%1' has been moved to '%2'", oldName, child.getDisplayName()), SKGDocument::Hidden))
1188             }
1189         }
1190     } else if (iData->hasFormat(QStringLiteral("application/skg.payee.ids"))) {
1191         QByteArray encodedData = iData->data(QStringLiteral("application/skg.payee.ids"));
1192         QDataStream stream(&encodedData, QIODevice::ReadOnly);
1193         QStringList newItems;
1194 
1195         if (iParent.isValid()) {
1196             SKGBEGINTRANSACTION(*getDocument(), i18nc("Noun, name of the user action", "Merge payees"), err)
1197             SKGPayeeObject parentPayee(getObject(iParent));
1198             while (!stream.atEnd() && !err) {
1199                 int o_id;
1200                 QString o_table;
1201                 stream >> o_table;
1202                 stream >> o_id;
1203 
1204                 SKGPayeeObject child(getDocument(), o_id);
1205                 err = child.load();
1206 
1207                 // Send message
1208                 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))
1209 
1210                 IFOKDO(err, parentPayee.merge(child))
1211             }
1212         }
1213     } else if (iData->hasFormat(QStringLiteral("application/skg.account.ids"))) {
1214         QByteArray encodedData = iData->data(QStringLiteral("application/skg.account.ids"));
1215         QDataStream stream(&encodedData, QIODevice::ReadOnly);
1216         QStringList newItems;
1217 
1218         if (iParent.isValid()) {
1219             SKGBEGINTRANSACTION(*getDocument(), i18nc("Noun, name of the user action", "Merge accounts"), err)
1220             SKGAccountObject parentAccount(getObject(iParent));
1221             while (!stream.atEnd() && !err) {
1222                 int o_id;
1223                 QString o_table;
1224                 stream >> o_table;
1225                 stream >> o_id;
1226 
1227                 SKGAccountObject child(getDocument(), o_id);
1228                 err = child.load();
1229 
1230                 // Send message
1231                 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))
1232 
1233                 double balancebefore = 0.0;
1234                 double balanceafter = 0.0;
1235                 SKGUnitObject unit2;
1236                 IFOKDO(err, parentAccount.getInitialBalance(balancebefore, unit2))
1237                 IFOKDO(err, parentAccount.merge(child, !(QApplication::keyboardModifiers() &Qt::ControlModifier)))
1238                 IFOKDO(err, parentAccount.getInitialBalance(balanceafter, unit2))
1239                 if (balanceafter != balancebefore) {
1240                     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);
1241                 }
1242             }
1243         }
1244     } else if (iData->hasFormat(QStringLiteral("application/skg.unit.ids"))) {
1245         QByteArray encodedData = iData->data(QStringLiteral("application/skg.unit.ids"));
1246         QDataStream stream(&encodedData, QIODevice::ReadOnly);
1247         QStringList newItems;
1248 
1249         if (iParent.isValid()) {
1250             SKGBEGINTRANSACTION(*getDocument(), i18nc("Noun, name of the user action", "Merge units"), err)
1251             SKGUnitObject parentUnit(getObject(iParent));
1252             while (!stream.atEnd() && !err) {
1253                 int o_id;
1254                 QString o_table;
1255                 stream >> o_table;
1256                 stream >> o_id;
1257 
1258                 SKGUnitObject child(getDocument(), o_id);
1259                 err = child.load();
1260 
1261                 // Send message
1262                 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))
1263 
1264                 IFOKDO(err, parentUnit.merge(child))
1265             }
1266         }
1267     } else if (iData->hasFormat(QStringLiteral("application/skg.refund.ids"))) {
1268         QByteArray encodedData = iData->data(QStringLiteral("application/skg.refund.ids"));
1269         QDataStream stream(&encodedData, QIODevice::ReadOnly);
1270         QStringList newItems;
1271 
1272         if (iParent.isValid()) {
1273             SKGBEGINTRANSACTION(*getDocument(), i18nc("Noun, name of the user action", "Merge trackers"), err)
1274             SKGTrackerObject parentTracker(getObject(iParent));
1275             while (!stream.atEnd() && !err) {
1276                 int o_id;
1277                 QString o_table;
1278                 stream >> o_table;
1279                 stream >> o_id;
1280 
1281                 SKGTrackerObject child(getDocument(), o_id);
1282                 err = child.load();
1283 
1284                 // Send message
1285                 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))
1286 
1287                 IFOKDO(err, parentTracker.merge(child))
1288             }
1289         }
1290     }
1291     SKGMainPanel::displayErrorMessage(err);
1292     return !err;
1293 }
1294 
1295 void SKGObjectModel::dataModified(const QString& iTableName, int iIdTransaction)
1296 {
1297     if (getRealTable() == iTableName || iTableName.isEmpty() || getRealTable() == QStringLiteral("doctransaction")) {
1298         SKGTRACEINFUNC(1)
1299         if (iTableName == QStringLiteral("category")) {
1300             // Full refresh
1301             m_isResetRealyNeeded = true;
1302             refresh();
1303         } else {
1304             SKGObjectModelBase::dataModified(iTableName, iIdTransaction);
1305         }
1306     } else {
1307         SKGObjectModelBase::dataModified(iTableName, iIdTransaction);
1308     }
1309 }
1310 
1311 QString SKGObjectModel::formatMoney(double iValue) const
1312 {
1313     return getDocument()->formatMoney(iValue, qobject_cast<SKGDocumentBank*>(getDocument())->getPrimaryUnit(), false);
1314 }
1315 
1316 
1317