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 }