File indexing completed on 2024-05-12 16:43:57

0001 /*
0002     SPDX-FileCopyrightText: 2006 Darren Gould <darren_gould@gmx.de>
0003     SPDX-FileCopyrightText: 2009-2014 Alvaro Soliverez <asoliverez@gmail.com>
0004     SPDX-FileCopyrightText: 2017-2018 Łukasz Wojniłowicz <lukasz.wojnilowicz@gmail.com>
0005     SPDX-FileCopyrightText: 2020 Robert Szczesiak <dev.rszczesiak@gmail.com>
0006     SPDX-License-Identifier: GPL-2.0-or-later
0007 */
0008 
0009 #include "budgetviewproxymodel.h"
0010 #include "accountsviewproxymodel_p.h"
0011 
0012 // ----------------------------------------------------------------------------
0013 // QT Includes
0014 
0015 // ----------------------------------------------------------------------------
0016 // KDE Includes
0017 
0018 // ----------------------------------------------------------------------------
0019 // Project Includes
0020 
0021 #include "mymoneyutils.h"
0022 #include "mymoneyfile.h"
0023 #include "mymoneyaccount.h"
0024 #include "mymoneysecurity.h"
0025 #include "mymoneymoney.h"
0026 #include "mymoneybudget.h"
0027 #include "models.h"
0028 #include "accountsmodel.h"
0029 #include "modelenums.h"
0030 
0031 using namespace eAccountsModel;
0032 
0033 class BudgetViewProxyModelPrivate : public AccountsViewProxyModelPrivate
0034 {
0035     Q_DISABLE_COPY(BudgetViewProxyModelPrivate)
0036 
0037 public:
0038     BudgetViewProxyModelPrivate() :
0039         AccountsViewProxyModelPrivate()
0040     {
0041     }
0042 
0043     ~BudgetViewProxyModelPrivate() override
0044     {
0045     }
0046 
0047     MyMoneyBudget m_budget;
0048     MyMoneyMoney m_lastBalance;
0049 };
0050 
0051 BudgetViewProxyModel::BudgetViewProxyModel(QObject *parent) :
0052     AccountsViewProxyModel(*new BudgetViewProxyModelPrivate, parent)
0053 {
0054     setFilterCaseSensitivity(Qt::CaseInsensitive);
0055 }
0056 
0057 BudgetViewProxyModel::~BudgetViewProxyModel()
0058 {
0059 }
0060 
0061 /**
0062   * This function was reimplemented to add the data needed by the other columns that this model
0063   * is adding besides the columns of the @ref AccountsModel.
0064   */
0065 QVariant BudgetViewProxyModel::data(const QModelIndex &index, int role) const
0066 {
0067     Q_D(const BudgetViewProxyModel);
0068     if (!MyMoneyFile::instance()->storageAttached())
0069         return QVariant();
0070     const auto sourceColumn = d->m_mdlColumns->at(mapToSource(index).column());
0071     auto const file = MyMoneyFile::instance();
0072     const auto ixAccount = mapToSource(BudgetViewProxyModel::index(index.row(), static_cast<int>(Column::Account), index.parent()));
0073     const auto account = ixAccount.data((int)Role::Account).value<MyMoneyAccount>();
0074 
0075     static QVector<Column> columnsToProcess {Column::TotalBalance, Column::TotalValue/*, AccountsModel::PostedValue, Column::Account*/};
0076     if (columnsToProcess.contains(sourceColumn)) {
0077         switch (role) {
0078         case Qt::DisplayRole:
0079         {
0080             switch (sourceColumn) {
0081             case Column::TotalBalance:
0082                 if (file->security(account.currencyId()) != file->baseCurrency())
0083                     return QVariant(MyMoneyUtils::formatMoney(accountBalance(account.id()), file->security(account.currencyId())));
0084                 else
0085                     return QVariant();
0086             case Column::TotalValue:
0087                 return QVariant(MyMoneyUtils::formatMoney(computeTotalValue(ixAccount), file->baseCurrency()));
0088             // FIXME: Posted value doesn't correspond with total value without below code. Investigate why and wheather it matters.
0089             //              case AccountsModel::PostedValue:
0090             //                return QVariant(MyMoneyUtils::formatMoney(accountValue(account, accountBalance(account.id())), file->baseCurrency()));
0091             default:
0092                 break;
0093             }
0094             break;
0095         }
0096         default:
0097             break;
0098         }
0099     }
0100     switch (role) {
0101     case (int)Role::Balance:
0102         if (file->security(account.currencyId()) != file->baseCurrency())
0103             return QVariant::fromValue(accountBalance(account.id()));
0104         else
0105             return QVariant();
0106     case (int)Role::TotalValue:
0107         return QVariant::fromValue(computeTotalValue(ixAccount));
0108     case (int)Role::Value:
0109         return QVariant::fromValue(accountValue(account, accountBalance(account.id())));
0110     default:
0111         break;
0112     }
0113     return AccountsViewProxyModel::data(index, role);
0114 }
0115 
0116 Qt::ItemFlags BudgetViewProxyModel::flags(const QModelIndex &index) const
0117 {
0118     Q_D(const BudgetViewProxyModel);
0119     Qt::ItemFlags flags = AccountsViewProxyModel::flags(index);
0120     if (!index.parent().isValid())
0121         return flags & ~Qt::ItemIsSelectable;
0122 
0123     // check if any of the parent accounts has the 'include subaccounts'
0124     // flag set. If so, we don't allow selecting this account
0125     QModelIndex idx = index.parent();
0126     while (idx.isValid()) {
0127         QModelIndex source_idx = mapToSource(idx);
0128         QVariant accountData = sourceModel()->data(source_idx, (int)Role::Account);
0129         if (accountData.canConvert<MyMoneyAccount>()) {
0130             MyMoneyAccount account = accountData.value<MyMoneyAccount>();
0131             // find out if the account is budgeted
0132             MyMoneyBudget::AccountGroup budgetAccount = d->m_budget.account(account.id());
0133             if (budgetAccount.id() == account.id()) {
0134                 if (budgetAccount.budgetSubaccounts()) {
0135                     return flags & ~Qt::ItemIsEnabled;
0136                 }
0137             }
0138         }
0139         idx = idx.parent();
0140     }
0141     return flags;
0142 }
0143 
0144 void BudgetViewProxyModel::setBudget(const MyMoneyBudget& budget)
0145 {
0146     Q_D(BudgetViewProxyModel);
0147     d->m_budget = budget;
0148     invalidate();
0149     checkBalance();
0150 }
0151 
0152 bool BudgetViewProxyModel::filterAcceptsRow(int source_row, const QModelIndex &source_parent) const
0153 {
0154     Q_D(const BudgetViewProxyModel);
0155     const auto index = sourceModel()->index(source_row, static_cast<int>(Column::Account), source_parent);
0156     const auto accountData = sourceModel()->data(index, (int)Role::Account);
0157     if (accountData.canConvert<MyMoneyAccount>()) {
0158         const auto account = accountData.value<MyMoneyAccount>();
0159         if (!account.isIncomeExpense()) {
0160             return false;
0161         }
0162         if (hideUnusedIncomeExpenseAccounts()) {
0163             MyMoneyMoney balance;
0164             // find out if the account is budgeted
0165             const auto budgetAccount = d->m_budget.account(account.id());
0166             if (budgetAccount.id() == account.id()) {
0167                 balance = budgetAccount.balance();
0168                 switch (budgetAccount.budgetLevel()) {
0169                 case eMyMoney::Budget::Level::Monthly:
0170                     balance *= MyMoneyMoney(12);
0171                     break;
0172                 default:
0173                     break;
0174                 }
0175             }
0176             if (!balance.isZero())
0177                 return AccountsViewProxyModel::filterAcceptsRow(source_row, source_parent);
0178             for (auto i = 0; i < sourceModel()->rowCount(index); ++i) {
0179                 if (filterAcceptsRow(i, index))
0180                     return true;
0181             }
0182             return false;
0183         }
0184         return AccountsViewProxyModel::filterAcceptsRow(source_row, source_parent);
0185     }
0186     return false;
0187 }
0188 
0189 MyMoneyMoney BudgetViewProxyModel::accountBalance(const QString &accountId) const
0190 {
0191     Q_D(const BudgetViewProxyModel);
0192     MyMoneyMoney balance;
0193     // find out if the account is budgeted
0194     MyMoneyBudget::AccountGroup budgetAccount = d->m_budget.account(accountId);
0195     if (budgetAccount.id() == accountId) {
0196         balance = budgetAccount.balance();
0197         switch (budgetAccount.budgetLevel()) {
0198         case eMyMoney::Budget::Level::Monthly:
0199             balance *= MyMoneyMoney(12);
0200             break;
0201         default:
0202             break;
0203         }
0204     }
0205     return balance;
0206 }
0207 
0208 MyMoneyMoney BudgetViewProxyModel::accountValue(const MyMoneyAccount &account, const MyMoneyMoney &balance) const
0209 {
0210     return Models::instance()->accountsModel()->accountValue(account, balance);
0211 }
0212 
0213 MyMoneyMoney BudgetViewProxyModel::computeTotalValue(const QModelIndex &source_index) const
0214 {
0215     auto model = sourceModel();
0216     auto account = model->data(source_index, (int)Role::Account).value<MyMoneyAccount>();
0217     auto totalValue = accountValue(account, accountBalance(account.id()));
0218     for (auto i = 0; i < model->rowCount(source_index); ++i)
0219         totalValue += computeTotalValue(model->index(i, static_cast<int>(Column::Account), source_index));
0220     return totalValue;
0221 }
0222 
0223 void BudgetViewProxyModel::checkBalance()
0224 {
0225     Q_D(BudgetViewProxyModel);
0226     // compute the balance
0227     QModelIndexList incomeList = match(index(0, 0),
0228                                        (int)Role::ID,
0229                                        MyMoneyFile::instance()->income().id(),
0230                                        1,
0231                                        Qt::MatchFlags(Qt::MatchExactly | Qt::MatchCaseSensitive | Qt::MatchWrap));
0232 
0233     QModelIndexList expenseList = match(index(0, 0),
0234                                         (int)Role::ID,
0235                                         MyMoneyFile::instance()->expense().id(),
0236                                         1,
0237                                         Qt::MatchFlags(Qt::MatchExactly | Qt::MatchCaseSensitive | Qt::MatchWrap));
0238 
0239     MyMoneyMoney balance;
0240     if (!incomeList.isEmpty() && !expenseList.isEmpty()) {
0241         QVariant incomeValue = data(incomeList.front(), (int)Role::TotalValue);
0242         QVariant expenseValue = data(expenseList.front(), (int)Role::TotalValue);
0243 
0244         if (incomeValue.isValid() && expenseValue.isValid()) {
0245             balance = incomeValue.value<MyMoneyMoney>() - expenseValue.value<MyMoneyMoney>();
0246         }
0247     }
0248     if (d->m_lastBalance != balance) {
0249         d->m_lastBalance = balance;
0250         emit balanceChanged(d->m_lastBalance);
0251     }
0252 }