File indexing completed on 2024-05-12 16:42:33

0001 /*
0002     SPDX-FileCopyrightText: 2000-2002 Michael Edwardes <mte@users.sourceforge.net>
0003     SPDX-FileCopyrightText: 2001 Felix Rodriguez <frodriguez@users.sourceforge.net>
0004     SPDX-FileCopyrightText: 2002-2003 Kevin Tambascio <ktambascio@users.sourceforge.net>
0005     SPDX-FileCopyrightText: 2006-2017 Thomas Baumgart <tbaumgart@kde.org>
0006     SPDX-FileCopyrightText: 2006 Ace Jones <acejones@users.sourceforge.net>
0007     SPDX-FileCopyrightText: 2017-2018 Łukasz Wojniłowicz <lukasz.wojnilowicz@gmail.com>
0008     SPDX-License-Identifier: GPL-2.0-or-later
0009 */
0010 
0011 #include "mymoneyaccount.h"
0012 #include "mymoneyaccount_p.h"
0013 
0014 // ----------------------------------------------------------------------------
0015 // QT Includes
0016 
0017 #include <QRegExp>
0018 #include <QPixmap>
0019 #include <QPixmapCache>
0020 #include <QPainter>
0021 #include <QIcon>
0022 #include <QDebug>
0023 
0024 // ----------------------------------------------------------------------------
0025 // KDE Includes
0026 
0027 #include <KLocalizedString>
0028 
0029 // ----------------------------------------------------------------------------
0030 // Project Includes
0031 
0032 #include "mymoneyutils.h"
0033 #include "mymoneyexception.h"
0034 #include "mymoneysplit.h"
0035 #include "mymoneyfile.h"
0036 #include "mymoneysecurity.h"
0037 #include "mymoneyinstitution.h"
0038 #include "mymoneypayee.h"
0039 #include "payeeidentifier/payeeidentifiertyped.h"
0040 #include "payeeidentifier/ibanbic/ibanbic.h"
0041 #include "payeeidentifier/nationalaccount/nationalaccount.h"
0042 #include "icons/icons.h"
0043 
0044 using namespace Icons;
0045 
0046 MyMoneyAccount::MyMoneyAccount() :
0047     MyMoneyObject(*new MyMoneyAccountPrivate),
0048     MyMoneyKeyValueContainer()
0049 {
0050 }
0051 
0052 MyMoneyAccount::MyMoneyAccount(const QString &id):
0053     MyMoneyObject(*new MyMoneyAccountPrivate, id),
0054     MyMoneyKeyValueContainer()
0055 {
0056 }
0057 
0058 MyMoneyAccount::MyMoneyAccount(const MyMoneyAccount& other) :
0059     MyMoneyObject(*new MyMoneyAccountPrivate(*other.d_func()), other.id()),
0060     MyMoneyKeyValueContainer(other)
0061 {
0062 }
0063 
0064 MyMoneyAccount::MyMoneyAccount(const QString& id, const MyMoneyAccount& other) :
0065     MyMoneyObject(*new MyMoneyAccountPrivate(*other.d_func()), id),
0066     MyMoneyKeyValueContainer(other)
0067 {
0068 }
0069 
0070 MyMoneyAccount::~MyMoneyAccount()
0071 {
0072 }
0073 
0074 void MyMoneyAccount::touch()
0075 {
0076     setLastModified(QDate::currentDate());
0077 }
0078 
0079 eMyMoney::Account::Type MyMoneyAccount::accountType() const
0080 {
0081     Q_D(const MyMoneyAccount);
0082     return d->m_accountType;
0083 }
0084 
0085 void MyMoneyAccount::setAccountType(const Account::Type type)
0086 {
0087     Q_D(MyMoneyAccount);
0088     d->m_accountType = type;
0089 }
0090 
0091 QString MyMoneyAccount::institutionId() const
0092 {
0093     Q_D(const MyMoneyAccount);
0094     return d->m_institution;
0095 }
0096 
0097 void MyMoneyAccount::setInstitutionId(const QString& id)
0098 {
0099     Q_D(MyMoneyAccount);
0100     d->m_institution = id;
0101 }
0102 
0103 QString MyMoneyAccount::name() const
0104 {
0105     Q_D(const MyMoneyAccount);
0106     return d->m_name;
0107 }
0108 
0109 void MyMoneyAccount::setName(const QString& name)
0110 {
0111     Q_D(MyMoneyAccount);
0112     d->m_name = name;
0113 }
0114 
0115 QString MyMoneyAccount::number() const
0116 {
0117     Q_D(const MyMoneyAccount);
0118     return d->m_number;
0119 }
0120 
0121 void MyMoneyAccount::setNumber(const QString& number)
0122 {
0123     Q_D(MyMoneyAccount);
0124     d->m_number = number;
0125 }
0126 
0127 QString MyMoneyAccount::description() const
0128 {
0129     Q_D(const MyMoneyAccount);
0130     return d->m_description;
0131 }
0132 
0133 void MyMoneyAccount::setDescription(const QString& desc)
0134 {
0135     Q_D(MyMoneyAccount);
0136     d->m_description = desc;
0137 }
0138 
0139 QDate MyMoneyAccount::openingDate() const
0140 {
0141     Q_D(const MyMoneyAccount);
0142     return d->m_openingDate;
0143 }
0144 
0145 void MyMoneyAccount::setOpeningDate(const QDate& date)
0146 {
0147     Q_D(MyMoneyAccount);
0148     d->m_openingDate = date;
0149 }
0150 
0151 QDate MyMoneyAccount::lastReconciliationDate() const
0152 {
0153     Q_D(const MyMoneyAccount);
0154     return d->m_lastReconciliationDate;
0155 }
0156 
0157 void MyMoneyAccount::setLastReconciliationDate(const QDate& date)
0158 {
0159     Q_D(MyMoneyAccount);
0160     d->m_lastReconciliationDate = date;
0161 }
0162 
0163 QDate MyMoneyAccount::lastModified() const
0164 {
0165     Q_D(const MyMoneyAccount);
0166     return d->m_lastModified;
0167 }
0168 
0169 void MyMoneyAccount::setLastModified(const QDate& date)
0170 {
0171     Q_D(MyMoneyAccount);
0172     d->m_lastModified = date;
0173 }
0174 
0175 QString MyMoneyAccount::parentAccountId() const
0176 {
0177     Q_D(const MyMoneyAccount);
0178     return d->m_parentAccount;
0179 }
0180 
0181 void MyMoneyAccount::setParentAccountId(const QString& parent)
0182 {
0183     Q_D(MyMoneyAccount);
0184     d->m_parentAccount = parent;
0185 }
0186 
0187 QStringList MyMoneyAccount::accountList() const
0188 {
0189     Q_D(const MyMoneyAccount);
0190     return d->m_accountList;
0191 }
0192 
0193 int MyMoneyAccount::accountCount() const
0194 {
0195     Q_D(const MyMoneyAccount);
0196     return d->m_accountList.count();
0197 }
0198 
0199 void MyMoneyAccount::addAccountId(const QString& account)
0200 {
0201     Q_D(MyMoneyAccount);
0202     if (!d->m_accountList.contains(account))
0203         d->m_accountList += account;
0204 }
0205 
0206 void MyMoneyAccount::removeAccountIds()
0207 {
0208     Q_D(MyMoneyAccount);
0209     d->m_accountList.clear();
0210 }
0211 
0212 void MyMoneyAccount::removeAccountId(const QString& account)
0213 {
0214     Q_D(MyMoneyAccount);
0215     const auto pos = d->m_accountList.indexOf(account);
0216     if (pos != -1)
0217         d->m_accountList.removeAt(pos);
0218 }
0219 
0220 bool MyMoneyAccount::operator == (const MyMoneyAccount& right) const
0221 {
0222     Q_D(const MyMoneyAccount);
0223     auto d2 = static_cast<const MyMoneyAccountPrivate *>(right.d_func());
0224     // clang-format off
0225     return (MyMoneyKeyValueContainer::operator==(right)
0226             && MyMoneyObject::operator==(right)
0227             && (d->m_accountList == d2->m_accountList)
0228             && (d->m_accountType == d2->m_accountType)
0229             && (d->m_lastModified == d2->m_lastModified)
0230             && (d->m_lastReconciliationDate == d2->m_lastReconciliationDate)
0231             && ((d->m_name.length() == 0 && d2->m_name.length() == 0) || (d->m_name == d2->m_name))
0232             && ((d->m_number.length() == 0 && d2->m_number.length() == 0) || (d->m_number == d2->m_number))
0233             && ((d->m_description.length() == 0 && d2->m_description.length() == 0) || (d->m_description == d2->m_description))
0234             && (d->m_openingDate == d2->m_openingDate)
0235             && (d->m_parentAccount == d2->m_parentAccount)
0236             && (d->m_currencyId == d2->m_currencyId)
0237             && (d->m_institution == d2->m_institution));
0238     // clang-format on
0239 }
0240 
0241 Account::Type MyMoneyAccount::accountGroup(Account::Type type)
0242 {
0243     switch (type) {
0244     case Account::Type::Checkings:
0245     case Account::Type::Savings:
0246     case Account::Type::Cash:
0247     case Account::Type::Currency:
0248     case Account::Type::Investment:
0249     case Account::Type::MoneyMarket:
0250     case Account::Type::CertificateDep:
0251     case Account::Type::AssetLoan:
0252     case Account::Type::Stock:
0253         return Account::Type::Asset;
0254 
0255     case Account::Type::CreditCard:
0256     case Account::Type::Loan:
0257         return Account::Type::Liability;
0258 
0259     default:
0260         return type;
0261     }
0262 }
0263 
0264 Account::Type MyMoneyAccount::accountGroup() const
0265 {
0266     Q_D(const MyMoneyAccount);
0267     return accountGroup(d->m_accountType);
0268 }
0269 
0270 QString MyMoneyAccount::currencyId() const
0271 {
0272     Q_D(const MyMoneyAccount);
0273     return d->m_currencyId;
0274 }
0275 
0276 QString MyMoneyAccount::tradingCurrencyId() const
0277 {
0278     const auto file = MyMoneyFile::instance();
0279 
0280     // First, get the trading currency (formerly deep currency)
0281     auto deepcurrency = file->security(currencyId());
0282     if (!deepcurrency.isCurrency())
0283         deepcurrency = file->security(deepcurrency.tradingCurrency());
0284 
0285     // Return the trading currency's ID
0286     return deepcurrency.id();
0287 }
0288 
0289 bool MyMoneyAccount::isForeignCurrency() const
0290 {
0291     return (tradingCurrencyId() != MyMoneyFile::instance()->baseCurrency().id());
0292 }
0293 
0294 void MyMoneyAccount::setCurrencyId(const QString& id)
0295 {
0296     Q_D(MyMoneyAccount);
0297     d->m_currencyId = id;
0298 }
0299 
0300 bool MyMoneyAccount::isAssetLiability() const
0301 {
0302     return accountGroup() == Account::Type::Asset || accountGroup() == Account::Type::Liability;
0303 }
0304 
0305 bool MyMoneyAccount::isIncomeExpense() const
0306 {
0307     return accountGroup() == Account::Type::Income || accountGroup() == Account::Type::Expense;
0308 }
0309 
0310 bool MyMoneyAccount::isLoan() const
0311 {
0312     return accountType() == Account::Type::Loan || accountType() == Account::Type::AssetLoan;
0313 }
0314 
0315 bool MyMoneyAccount::isInvest() const
0316 {
0317     return accountType() == Account::Type::Stock;
0318 }
0319 
0320 bool MyMoneyAccount::isLiquidAsset() const
0321 {
0322     return accountType() == Account::Type::Checkings //
0323             || accountType() == Account::Type::Savings //
0324             || accountType() == Account::Type::Cash;
0325 }
0326 
0327 bool MyMoneyAccount::isLiquidLiability() const
0328 {
0329     return accountType() == Account::Type::CreditCard;
0330 }
0331 
0332 bool MyMoneyAccount::isCostCenterRequired() const
0333 {
0334     return value("CostCenter").toLower() == QLatin1String("yes");
0335 }
0336 
0337 void MyMoneyAccount::setCostCenterRequired(bool required)
0338 {
0339     if(required) {
0340         setValue("CostCenter", "yes");
0341     } else {
0342         deletePair("CostCenter");
0343     }
0344 }
0345 
0346 bool MyMoneyAccount::hasReferenceTo(const QString& id) const
0347 {
0348     Q_D(const MyMoneyAccount);
0349     return (id == d->m_institution) || (id == d->m_parentAccount) || (id == d->m_currencyId);
0350 }
0351 
0352 void MyMoneyAccount::setOnlineBankingSettings(const MyMoneyKeyValueContainer& values)
0353 {
0354     Q_D(MyMoneyAccount);
0355     d->m_onlineBankingSettings = values;
0356 }
0357 
0358 MyMoneyKeyValueContainer MyMoneyAccount::onlineBankingSettings() const
0359 {
0360     Q_D(const MyMoneyAccount);
0361     return d->m_onlineBankingSettings;
0362 }
0363 
0364 void MyMoneyAccount::setClosed(bool closed)
0365 {
0366     if (closed)
0367         setValue("mm-closed", "yes");
0368     else
0369         deletePair("mm-closed");
0370 }
0371 
0372 bool MyMoneyAccount::isClosed() const
0373 {
0374     return !(value("mm-closed").isEmpty());
0375 }
0376 
0377 int MyMoneyAccount::fraction(const MyMoneySecurity& sec) const
0378 {
0379     Q_D(const MyMoneyAccount);
0380     int fraction;
0381     if (d->m_accountType == Account::Type::Cash)
0382         fraction = sec.smallestCashFraction();
0383     else
0384         fraction = sec.smallestAccountFraction();
0385     return fraction;
0386 }
0387 
0388 int MyMoneyAccount::fraction(const MyMoneySecurity& sec)
0389 {
0390     Q_D(MyMoneyAccount);
0391     if (d->m_accountType == Account::Type::Cash)
0392         d->m_fraction = sec.smallestCashFraction();
0393     else
0394         d->m_fraction = sec.smallestAccountFraction();
0395     return d->m_fraction;
0396 }
0397 
0398 int MyMoneyAccount::fraction() const
0399 {
0400     Q_D(const MyMoneyAccount);
0401     return d->m_fraction;
0402 }
0403 
0404 bool MyMoneyAccount::isCategory() const
0405 {
0406     Q_D(const MyMoneyAccount);
0407     return d->m_accountType == Account::Type::Income || d->m_accountType == Account::Type::Expense;
0408 }
0409 
0410 QString MyMoneyAccount::brokerageName() const
0411 {
0412     Q_D(const MyMoneyAccount);
0413     if (d->m_accountType == Account::Type::Investment)
0414         return QString("%1 (%2)").arg(d->m_name, i18nc("Brokerage (suffix for account names)", "Brokerage"));
0415     return d->m_name;
0416 }
0417 
0418 MyMoneyMoney MyMoneyAccount::balance() const
0419 {
0420     Q_D(const MyMoneyAccount);
0421     return d->m_balance;
0422 }
0423 
0424 void MyMoneyAccount::setBalance(const MyMoneyMoney& val)
0425 {
0426     Q_D(MyMoneyAccount);
0427     d->m_balance = val;
0428 }
0429 
0430 QPixmap MyMoneyAccount::accountPixmap(const bool reconcileFlag, const int size) const
0431 {
0432     static const QHash<Account::Type, Icon> accToIco{{Account::Type::Asset, Icon::Asset},
0433                                                      {Account::Type::Investment, Icon::Investment},
0434                                                      {Account::Type::Stock, Icon::Stock},
0435                                                      {Account::Type::MoneyMarket, Icon::Stock},
0436                                                      {Account::Type::Checkings, Icon::Checking},
0437                                                      {Account::Type::Savings, Icon::Savings},
0438                                                      {Account::Type::AssetLoan, Icon::LoanAsset},
0439                                                      {Account::Type::Loan, Icon::Loan},
0440                                                      {Account::Type::CreditCard, Icon::CreditCard},
0441                                                      {Account::Type::Asset, Icon::Asset},
0442                                                      {Account::Type::Cash, Icon::Cash},
0443                                                      {Account::Type::Income, Icon::Income},
0444                                                      {Account::Type::Expense, Icon::Expense},
0445                                                      {Account::Type::Equity, Icon::Equity}};
0446 
0447     Icon ixIcon = accToIco.value(accountType(), Icon::Liability);
0448 
0449     QString kyIcon = accountTypeToString(accountType()) + QString::number(size);
0450     QPixmap pxIcon;
0451 
0452     if (!QPixmapCache::find(kyIcon, &pxIcon)) {
0453         pxIcon = Icons::get(ixIcon).pixmap(size); // Qt::AA_UseHighDpiPixmaps (in Qt 5.7) doesn't return highdpi pixmap
0454         QPixmapCache::insert(kyIcon, pxIcon);
0455     }
0456 
0457     if (isClosed())
0458         ixIcon = Icon::AccountClosed;
0459     else if (reconcileFlag)
0460         ixIcon = Icon::Reconciled;
0461     else if (hasOnlineMapping())
0462         ixIcon = Icon::Download;
0463     else
0464         return pxIcon;
0465 
0466     QPixmap pxOverlay = Icons::get(ixIcon).pixmap(size);
0467     QPainter pxPainter(&pxIcon);
0468     const QSize szIcon = pxIcon.size();
0469     pxPainter.drawPixmap(szIcon.width() / 2, szIcon.height() / 2,
0470                          szIcon.width() / 2, szIcon.height() / 2, pxOverlay);
0471     return pxIcon;
0472 }
0473 
0474 QString MyMoneyAccount::accountTypeToString(const Account::Type accountType)
0475 {
0476     switch (accountType) {
0477     case Account::Type::Checkings:
0478         return i18nc("Account type", "Checking");
0479     case Account::Type::Savings:
0480         return i18nc("Account type", "Savings");
0481     case Account::Type::CreditCard:
0482         return i18nc("Account type", "Credit Card");
0483     case Account::Type::Cash:
0484         return i18nc("Account type", "Cash");
0485     case Account::Type::Loan:
0486         return i18nc("Account type", "Loan");
0487     case Account::Type::CertificateDep:
0488         return i18nc("Account type", "Certificate of Deposit");
0489     case Account::Type::Investment:
0490         return i18nc("Account type", "Investment");
0491     case Account::Type::MoneyMarket:
0492         return i18nc("Account type", "Money Market");
0493     case Account::Type::Asset:
0494         return i18nc("Account type", "Asset");
0495     case Account::Type::Liability:
0496         return i18nc("Account type", "Liability");
0497     case Account::Type::Currency:
0498         return i18nc("Account type", "Currency");
0499     case Account::Type::Income:
0500         return i18nc("Account type", "Income");
0501     case Account::Type::Expense:
0502         return i18nc("Account type", "Expense");
0503     case Account::Type::AssetLoan:
0504         return i18nc("Account type", "Investment Loan");
0505     case Account::Type::Stock:
0506         return i18nc("Account type", "Stock");
0507     case Account::Type::Equity:
0508         return i18nc("Account type", "Equity");
0509     default:
0510         return i18nc("Account type", "Unknown");
0511     }
0512 }
0513 
0514 bool MyMoneyAccount::addReconciliation(const QDate& date, const MyMoneyMoney& amount)
0515 {
0516     Q_D(MyMoneyAccount);
0517     // make sure, that history has been loaded
0518     reconciliationHistory();
0519 
0520     d->m_reconciliationHistory[date] = amount;
0521     QString history, sep;
0522     QMap<QDate, MyMoneyMoney>::const_iterator it;
0523     for (it = d->m_reconciliationHistory.constBegin();
0524             it != d->m_reconciliationHistory.constEnd();
0525             ++it) {
0526 
0527         history += QString("%1%2:%3").arg(sep,
0528                                           it.key().toString(Qt::ISODate),
0529                                           (*it).toString());
0530         sep = QLatin1Char(';');
0531     }
0532     setValue("reconciliationHistory", history);
0533     return true;
0534 }
0535 
0536 QMap<QDate, MyMoneyMoney> MyMoneyAccount::reconciliationHistory()
0537 {
0538     Q_D(MyMoneyAccount);
0539     // check if the internal history member is already loaded
0540     if (d->m_reconciliationHistory.count() == 0
0541             && !value("reconciliationHistory").isEmpty()) {
0542         QStringList entries = value("reconciliationHistory").split(';');
0543         foreach (const QString& entry, entries) {
0544             QStringList parts = entry.split(':');
0545             if (parts.count() == 2) {
0546                 QDate date = QDate::fromString(parts[0], Qt::ISODate);
0547                 MyMoneyMoney amount(parts[1]);
0548                 if (parts.count() == 2 && date.isValid()) {
0549                     d->m_reconciliationHistory[date] = amount;
0550                 }
0551             } else {
0552                 qDebug() << "Invalid reconciliationHistory" << entry;
0553             }
0554         }
0555     }
0556 
0557     return d->m_reconciliationHistory;
0558 }
0559 
0560 /**
0561  * @todo Improve setting of country for nationalAccount
0562  */
0563 QList< payeeIdentifier > MyMoneyAccount::payeeIdentifiers() const
0564 {
0565     QList< payeeIdentifier > list;
0566 
0567     MyMoneyFile* file = MyMoneyFile::instance();
0568 
0569     const auto strIBAN = QStringLiteral("iban");
0570     const auto strBIC = QStringLiteral("bic");
0571     // Iban & Bic
0572     if (!value(strIBAN).isEmpty()) {
0573         payeeIdentifierTyped<payeeIdentifiers::ibanBic> iban(new payeeIdentifiers::ibanBic);
0574         iban->setIban(value(strIBAN));
0575         iban->setBic(file->institution(institutionId()).value(strBIC));
0576         iban->setOwnerName(file->user().name());
0577         list.append(iban);
0578     }
0579 
0580     // National Account number
0581     if (!number().isEmpty()) {
0582         payeeIdentifierTyped<payeeIdentifiers::nationalAccount> national(new payeeIdentifiers::nationalAccount);
0583         national->setAccountNumber(number());
0584         national->setBankCode(file->institution(institutionId()).sortcode());
0585         if (file->user().state().length() == 2)
0586             national->setCountry(file->user().state());
0587         national->setOwnerName(file->user().name());
0588         list.append(national);
0589     }
0590 
0591     return list;
0592 }
0593 
0594 bool MyMoneyAccount::hasOnlineMapping() const
0595 {
0596     Q_D(const MyMoneyAccount);
0597     return !d->m_onlineBankingSettings.value(QLatin1String("provider")).isEmpty();
0598 }
0599 
0600 QString MyMoneyAccount::stdAccName(eMyMoney::Account::Standard stdAccID)
0601 {
0602     static const QHash<eMyMoney::Account::Standard, QString> stdAccNames {
0603         {eMyMoney::Account::Standard::Liability, QStringLiteral("AStd::Liability")},
0604         {eMyMoney::Account::Standard::Asset,     QStringLiteral("AStd::Asset")},
0605         {eMyMoney::Account::Standard::Expense,   QStringLiteral("AStd::Expense")},
0606         {eMyMoney::Account::Standard::Income,    QStringLiteral("AStd::Income")},
0607         {eMyMoney::Account::Standard::Equity,    QStringLiteral("AStd::Equity")},
0608     };
0609     return stdAccNames.value(stdAccID);
0610 }
0611 
0612 QString MyMoneyAccount::accountSeparator()
0613 {
0614     return QStringLiteral(":");
0615 }