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 }