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 }