File indexing completed on 2024-05-12 05:07:44
0001 /* 0002 SPDX-FileCopyrightText: 2019 Thomas Baumgart <tbaumgart@kde.org> 0003 SPDX-License-Identifier: GPL-2.0-or-later 0004 */ 0005 0006 0007 #include "ledgeraccountfilter.h" 0008 #include "ledgerfilterbase_p.h" 0009 0010 // ---------------------------------------------------------------------------- 0011 // QT Includes 0012 0013 // ---------------------------------------------------------------------------- 0014 // KDE Includes 0015 0016 #include <KDescendantsProxyModel> 0017 0018 // ---------------------------------------------------------------------------- 0019 // Project Includes 0020 0021 #include "accountsmodel.h" 0022 #include "journalmodel.h" 0023 #include "mymoneyaccount.h" 0024 #include "mymoneyenums.h" 0025 #include "mymoneyfile.h" 0026 #include "mymoneymoney.h" 0027 #include "onlinebalanceproxymodel.h" 0028 #include "reconciliationmodel.h" 0029 #include "schedulesjournalmodel.h" 0030 #include "securitiesmodel.h" 0031 #include "securityaccountsproxymodel.h" 0032 #include "specialdatesmodel.h" 0033 0034 class LedgerAccountFilterPrivate : public LedgerFilterBasePrivate 0035 { 0036 public: 0037 explicit LedgerAccountFilterPrivate(LedgerAccountFilter* qq) 0038 : LedgerFilterBasePrivate(qq) 0039 , onlinebalanceproxymodel(nullptr) 0040 , securityAccountsProxyModel(nullptr) 0041 { 0042 } 0043 0044 ~LedgerAccountFilterPrivate() 0045 { 0046 } 0047 0048 OnlineBalanceProxyModel* onlinebalanceproxymodel; 0049 SecurityAccountsProxyModel* securityAccountsProxyModel; 0050 0051 MyMoneyAccount account; 0052 }; 0053 0054 0055 LedgerAccountFilter::LedgerAccountFilter(QObject* parent, QVector<QAbstractItemModel*> specialJournalModels) 0056 : LedgerFilterBase(new LedgerAccountFilterPrivate(this), parent) 0057 { 0058 Q_D(LedgerAccountFilter); 0059 setMaintainBalances(true); 0060 setObjectName("LedgerAccountFilter"); 0061 0062 d->onlinebalanceproxymodel = new OnlineBalanceProxyModel(parent); 0063 d->securityAccountsProxyModel = new SecurityAccountsProxyModel(parent); 0064 0065 const auto accountsModel = MyMoneyFile::instance()->flatAccountsModel(); 0066 d->onlinebalanceproxymodel->setObjectName("OnlineBalanceProxyModel"); 0067 d->onlinebalanceproxymodel->setSourceModel(accountsModel); 0068 d->securityAccountsProxyModel->setObjectName("SecurityAccountsProxyModel"); 0069 d->securityAccountsProxyModel->setSourceModel(accountsModel); 0070 0071 d->concatModel->setObjectName("LedgerView concatModel"); 0072 d->concatModel->addSourceModel(MyMoneyFile::instance()->journalModel()); 0073 d->concatModel->addSourceModel(d->onlinebalanceproxymodel); 0074 d->concatModel->addSourceModel(d->securityAccountsProxyModel); 0075 0076 for (const auto model : specialJournalModels) { 0077 d->concatModel->addSourceModel(model); 0078 } 0079 0080 setFilterRole(eMyMoney::Model::SplitAccountIdRole); 0081 0082 setSourceModel(d->concatModel); 0083 } 0084 0085 LedgerAccountFilter::~LedgerAccountFilter() 0086 { 0087 } 0088 0089 void LedgerAccountFilter::setShowBalanceInverted(bool inverted) 0090 { 0091 Q_D(LedgerAccountFilter); 0092 d->showValuesInverted = inverted; 0093 } 0094 0095 void LedgerAccountFilter::setAccount(const MyMoneyAccount& acc) 0096 { 0097 Q_D(LedgerAccountFilter); 0098 0099 d->account = acc; 0100 0101 d->showValuesInverted = false; 0102 if(d->account.accountGroup() == eMyMoney::Account::Type::Liability 0103 || d->account.accountGroup() == eMyMoney::Account::Type::Income) { 0104 d->showValuesInverted = true; 0105 } 0106 0107 setAccountType(d->account.accountType()); 0108 setFilterFixedString(d->account.id()); 0109 0110 connect(d->concatModel, &QAbstractItemModel::modelReset, this, [&]() { 0111 Q_D(LedgerAccountFilter); 0112 const auto newAccount = MyMoneyFile::instance()->accountsModel()->itemById(d->account.id()); 0113 if (d->account.accountList() != newAccount.accountList()) { 0114 d->account = newAccount; 0115 invalidate(); 0116 } 0117 }); 0118 } 0119 0120 bool LedgerAccountFilter::filterAcceptsRow(int source_row, const QModelIndex& source_parent) const 0121 { 0122 Q_D(const LedgerAccountFilter); 0123 auto rc = LedgerFilterBase::filterAcceptsRow(source_row, source_parent); 0124 0125 // in case we don't have a match and the current account is an investment account 0126 // we check if the journal entry references a child account of the investment account 0127 // if so, we need to display the transaction 0128 if (d->account.accountType() == eMyMoney::Account::Type::Investment) { 0129 const auto idx = sourceModel()->index(source_row, 0, source_parent); 0130 if (!rc) { 0131 rc = d->account.accountList().contains(idx.data(eMyMoney::Model::SplitAccountIdRole).toString()); 0132 } 0133 // we never show reconciliation records in investment accounts 0134 if (rc) { 0135 rc = !d->isReconciliationModel(idx); 0136 } 0137 } 0138 return rc; 0139 } 0140 0141 void LedgerAccountFilter::doSort() 0142 { 0143 // we don't do any sorting on this model by design 0144 } 0145 0146 QVariant LedgerAccountFilter::data(const QModelIndex& index, int role) const 0147 { 0148 Q_D(const LedgerAccountFilter); 0149 if (role == eMyMoney::Model::ShowValueInvertedRole) { 0150 return d->showValuesInverted; 0151 } 0152 0153 switch (role) { 0154 case Qt::DisplayRole: 0155 if (index.column() == JournalModel::Balance) { 0156 if (index.row() < d->balances.size()) { 0157 // only report a balance for transactions and schedules but 0158 // not for the empty (new) transaction 0159 if (!index.data(eMyMoney::Model::IdRole).toString().isEmpty()) { 0160 const auto file = MyMoneyFile::instance(); 0161 const auto accountId = index.data(eMyMoney::Model::SplitAccountIdRole).toString(); 0162 const auto acc = file->accountsModel()->itemById(accountId); 0163 const auto security = file->securitiesModel()->itemById(acc.currencyId()); 0164 const auto fraction = 0165 (acc.accountType() == eMyMoney::Account::Type::Cash) ? security.smallestCashFraction() : security.smallestAccountFraction(); 0166 return d->balances.at(index.row()).formatMoney(fraction); 0167 } 0168 } 0169 return {}; 0170 } 0171 break; 0172 0173 case eMyMoney::Model::SplitReconcileDateRole: 0174 // we're asking for the reconciliation date of a special date 0175 // which maps to the next younger reconciliation date of that 0176 // account 0177 if (d->isSpecialDatesModel(index)) { 0178 QDate result; 0179 const auto entryDate = index.data(eMyMoney::Model::TransactionPostDateRole).toDate(); 0180 const auto reconciliationModel = MyMoneyFile::instance()->reconciliationModel(); 0181 const auto reconciliations = 0182 reconciliationModel->match(reconciliationModel->index(0, 0), 0183 eMyMoney::Model::SplitAccountIdRole, 0184 d->account.id(), 0185 -1, 0186 Qt::MatchFlags(Qt::MatchFlags(Qt::MatchExactly | Qt::MatchCaseSensitive | Qt::MatchRecursive))); 0187 for (const auto& idx : reconciliations) { 0188 const auto date = idx.data(role).toDate(); 0189 if (date >= entryDate) { 0190 if (!result.isValid() || (date < result)) { 0191 result = date; 0192 } 0193 } 0194 } 0195 return result; 0196 } 0197 break; 0198 0199 default: 0200 break; 0201 } 0202 0203 return LedgerFilterBase::data(index, role); 0204 }