File indexing completed on 2024-05-12 05:06:46
0001 /* 0002 SPDX-FileCopyrightText: 2002-2003 Michael Edwardes <mte@users.sourceforge.net> 0003 SPDX-FileCopyrightText: 2002-2016 Thomas Baumgart <tbaumgart@kde.org> 0004 SPDX-FileCopyrightText: 2002 Kevin Tambascio <ktambascio@users.sourceforge.net> 0005 SPDX-FileCopyrightText: 2017 Łukasz Wojniłowicz <lukasz.wojnilowicz@gmail.com> 0006 SPDX-License-Identifier: GPL-2.0-or-later 0007 */ 0008 0009 #include "mymoneyutils.h" 0010 0011 // ---------------------------------------------------------------------------- 0012 // QT Includes 0013 0014 #include <QDate> 0015 #include <QProcess> 0016 #include <QRegularExpression> 0017 #include <QTimeZone> 0018 0019 // ---------------------------------------------------------------------------- 0020 // KDE Headers 0021 0022 #include <KLocalizedString> 0023 0024 // ---------------------------------------------------------------------------- 0025 // Project Includes 0026 0027 #include "journalmodel.h" 0028 #include "mymoneyaccount.h" 0029 #include "mymoneyexception.h" 0030 #include "mymoneyfile.h" 0031 #include "mymoneymoney.h" 0032 #include "mymoneyschedule.h" 0033 #include "mymoneysecurity.h" 0034 #include "mymoneysplit.h" 0035 #include "mymoneytransaction.h" 0036 0037 QString MyMoneyUtils::getFileExtension(QString strFileName) 0038 { 0039 QString strTemp; 0040 if (!strFileName.isEmpty()) { 0041 //find last . deliminator 0042 int nLoc = strFileName.lastIndexOf('.'); 0043 if (nLoc != -1) { 0044 strTemp = strFileName.right(strFileName.length() - (nLoc + 1)); 0045 return strTemp.toUpper(); 0046 } 0047 } 0048 return strTemp; 0049 } 0050 0051 QString MyMoneyUtils::formatMoney(const MyMoneyMoney& val, 0052 const MyMoneyAccount& acc, 0053 const MyMoneySecurity& sec, 0054 bool showThousandSeparator) 0055 { 0056 return val.formatMoney(sec.tradingSymbol(), 0057 val.denomToPrec(acc.fraction()), 0058 showThousandSeparator); 0059 } 0060 0061 QString MyMoneyUtils::formatMoney(const MyMoneyMoney& val, 0062 const MyMoneySecurity& sec, 0063 bool showThousandSeparator) 0064 { 0065 return val.formatMoney(sec.tradingSymbol(), 0066 val.denomToPrec(sec.smallestAccountFraction()), 0067 showThousandSeparator); 0068 } 0069 0070 QString MyMoneyUtils::dateToString(const QDate& date) 0071 { 0072 if (!date.isNull() && date.isValid()) 0073 return date.toString(Qt::ISODate); 0074 0075 return QString(); 0076 } 0077 0078 QDate MyMoneyUtils::stringToDate(const QString& str) 0079 { 0080 if (!str.isEmpty()) { 0081 QDate date = QDate::fromString(str, Qt::ISODate); 0082 if (!date.isNull() && date.isValid()) 0083 return date; 0084 } 0085 return {}; 0086 } 0087 0088 QString MyMoneyUtils::dateTimeToString(const QDateTime& dateTime) 0089 { 0090 return QDateTime(dateTime.date(), dateTime.time(), QTimeZone(dateTime.offsetFromUtc())).toString(Qt::ISODate); 0091 } 0092 0093 QDateTime MyMoneyUtils::stringToDateTime(const QString& str) 0094 { 0095 if (!str.isEmpty()) { 0096 QDateTime dateTime = QDateTime::fromString(str, Qt::ISODate); 0097 if (!dateTime.isNull() && dateTime.isValid()) 0098 return dateTime; 0099 } 0100 return {}; 0101 } 0102 0103 QString MyMoneyUtils::QStringEmpty(const QString& val) 0104 { 0105 if (!val.isEmpty()) 0106 return QString(val); 0107 0108 return QString(); 0109 } 0110 0111 unsigned long MyMoneyUtils::extractId(const QString& txt) 0112 { 0113 static const QRegularExpression digitRegex("\\d+"); 0114 int pos; 0115 unsigned long rc = 0; 0116 0117 pos = txt.indexOf(digitRegex); 0118 if (pos != -1) { 0119 rc = txt.midRef(pos).toInt(); 0120 } 0121 return rc; 0122 } 0123 0124 void MyMoneyUtils::dissectTransaction(const MyMoneyTransaction& transaction, const MyMoneySplit& split, MyMoneySplit& assetAccountSplit, QList<MyMoneySplit>& feeSplits, QList<MyMoneySplit>& interestSplits, MyMoneySecurity& security, MyMoneySecurity& currency, eMyMoney::Split::InvestmentTransactionType& transactionType) 0125 { 0126 // collect the splits. split references the stock account and should already 0127 // be set up. assetAccountSplit references the corresponding asset account (maybe 0128 // empty), feeSplits is the list of all expenses and interestSplits 0129 // the list of all incomes 0130 assetAccountSplit = MyMoneySplit(); // set to none to check later if it was assigned 0131 auto file = MyMoneyFile::instance(); 0132 const auto splits = transaction.splits(); 0133 for (const auto& tsplit : splits) { 0134 auto acc = file->account(tsplit.accountId()); 0135 if (tsplit.id() == split.id()) { 0136 security = file->security(acc.currencyId()); 0137 } else if (acc.accountGroup() == eMyMoney::Account::Type::Expense) { 0138 feeSplits.append(tsplit); 0139 // feeAmount += tsplit.value(); 0140 } else if (acc.accountGroup() == eMyMoney::Account::Type::Income) { 0141 interestSplits.append(tsplit); 0142 // interestAmount += tsplit.value(); 0143 } else { 0144 if (assetAccountSplit == MyMoneySplit()) // first asset Account should be our requested brokerage account 0145 assetAccountSplit = tsplit; 0146 else if (tsplit.value().isNegative()) // the rest (if present) is handled as fee or interest 0147 feeSplits.append(tsplit); // and shouldn't be allowed to override assetAccountSplit 0148 else if (tsplit.value().isPositive()) 0149 interestSplits.append(tsplit); 0150 } 0151 } 0152 0153 // determine transaction type 0154 transactionType = split.investmentTransactionType(); 0155 if (transactionType == eMyMoney::Split::InvestmentTransactionType::UnknownTransactionType) { 0156 transactionType = eMyMoney::Split::InvestmentTransactionType::BuyShares; 0157 } 0158 0159 currency.setTradingSymbol("???"); 0160 try { 0161 currency = file->security(transaction.commodity()); 0162 } catch (const MyMoneyException &) { 0163 } 0164 } 0165 0166 Q_GLOBAL_STATIC(QString, dateTimeFormat); 0167 Q_GLOBAL_STATIC(QString, timeFormat); 0168 Q_GLOBAL_STATIC(QString, dateFormat); 0169 0170 QString MyMoneyUtils::formatDateTime(const QDateTime& dt) 0171 { 0172 if ((*dateTimeFormat).isEmpty()) { 0173 *dateTimeFormat = QLocale().dateTimeFormat(QLocale::ShortFormat); 0174 if (!(*dateTimeFormat).contains(QLatin1String("yyyy")) && (*dateTimeFormat).contains(QLatin1String("yy"))) { 0175 (*dateTimeFormat).replace(QLatin1String("yy"), QLatin1String("yyyy")); 0176 } 0177 } 0178 return dt.toString(*dateTimeFormat); 0179 } 0180 0181 QString MyMoneyUtils::formatTime(const QTime& time) 0182 { 0183 if ((*timeFormat).isEmpty()) { 0184 *timeFormat = QLocale().timeFormat(QLocale::LongFormat); 0185 } 0186 return time.toString(*timeFormat); 0187 } 0188 0189 QString MyMoneyUtils::formatDate(const QDate& date, QLocale::FormatType formatType) 0190 { 0191 if ((*dateFormat).isEmpty()) { 0192 *dateFormat = QLocale().dateFormat(formatType); 0193 if (!(*dateFormat).contains(QLatin1String("yyyy")) && (*dateFormat).contains(QLatin1String("yy"))) { 0194 (*dateFormat).replace(QLatin1String("yy"), QLatin1String("yyyy")); 0195 } 0196 } 0197 return date.toString(*dateFormat); 0198 } 0199 0200 void MyMoneyUtils::clearFormatCaches() 0201 { 0202 (*dateTimeFormat).clear(); 0203 (*timeFormat).clear(); 0204 (*dateFormat).clear(); 0205 } 0206 0207 QString MyMoneyUtils::paymentMethodToString(eMyMoney::Schedule::PaymentType paymentType) 0208 { 0209 return i18n(MyMoneySchedule::paymentMethodToString(paymentType)); 0210 } 0211 0212 modifyTransactionWarnLevel_t MyMoneyUtils::transactionWarnLevel(const QStringList& journalEntryIds) 0213 { 0214 modifyTransactionWarnLevel_t level = NoWarning; 0215 0216 const auto file = MyMoneyFile::instance(); 0217 const auto journalModel = file->journalModel(); 0218 const auto rows = journalModel->rowCount(); 0219 0220 QString lastTransactionId; 0221 0222 for (auto row = 0; row < rows; ++row) { 0223 const auto idx = journalModel->index(row, 0); 0224 if (idx.data(eMyMoney::Model::JournalTransactionIdRole).toString() != lastTransactionId) { 0225 if (journalEntryIds.contains(idx.data(eMyMoney::Model::IdRole).toString())) { 0226 modifyTransactionWarnLevel_t rc = NoWarning; 0227 try { 0228 const auto journalEntry = journalModel->itemByIndex(idx); 0229 const auto splits = journalEntry.transaction().splits(); 0230 for (const auto& split : qAsConst(splits)) { 0231 auto acc = file->account(split.accountId()); 0232 if (acc.isClosed()) 0233 rc = OneAccountClosed; 0234 else if (split.reconcileFlag() == eMyMoney::Split::State::Frozen) 0235 rc = OneSplitFrozen; 0236 else if (split.reconcileFlag() == eMyMoney::Split::State::Reconciled && rc < OneSplitReconciled) 0237 rc = OneSplitReconciled; 0238 } 0239 } catch (const MyMoneyException& e) { 0240 qDebug() << "Exception in MyMoneyUtils::transactionWarnLevel():" << e.what(); 0241 } 0242 lastTransactionId = idx.data(eMyMoney::Model::JournalTransactionIdRole).toString(); 0243 if (rc > level) { 0244 level = rc; 0245 } 0246 } 0247 } 0248 } 0249 return level; 0250 } 0251 0252 modifyTransactionWarnLevel_t MyMoneyUtils::transactionWarnLevel(const QString& journalEntryId) 0253 { 0254 return transactionWarnLevel(QStringList(journalEntryId)); 0255 } 0256 0257 QString MyMoneyUtils::convertWildcardToRegularExpression(const QString& pattern) 0258 { 0259 QString rc; 0260 bool insideBrackets = false; 0261 int pos = 0; 0262 int len = pattern.length(); 0263 0264 // insert an escape character if c == d 0265 auto escapeChar = [&](const QChar& d, const QChar& c) { 0266 if (c == d) { 0267 rc.append(QLatin1Char('\\')); 0268 } 0269 }; 0270 0271 while (pos < len) { 0272 bool skipInResult(false); 0273 const auto c = pattern[pos]; 0274 if (insideBrackets) { 0275 if (c == QLatin1Char(']')) { 0276 insideBrackets = false; 0277 } else { 0278 escapeChar(QLatin1Char('.'), c); 0279 escapeChar(QLatin1Char('?'), c); 0280 escapeChar(QLatin1Char('*'), c); 0281 } 0282 } else { 0283 if (c == QLatin1Char('[')) { 0284 insideBrackets = true; 0285 } else if (c == QLatin1Char('?')) { 0286 rc.append(QLatin1Char('.')); 0287 skipInResult = true; 0288 } else if (c == QLatin1Char('*')) { 0289 rc.append(QLatin1Char('.')); 0290 } else { 0291 escapeChar(QLatin1Char('.'), c); 0292 } 0293 } 0294 if (!skipInResult) { 0295 rc.append(c); 0296 } 0297 ++pos; 0298 } 0299 return rc; 0300 } 0301 0302 QString MyMoneyUtils::convertRegularExpressionToWildcard(const QString& pattern) 0303 { 0304 QString rc; 0305 int pos = 0; 0306 int len = pattern.length(); 0307 0308 while (pos < len) { 0309 auto c = pattern[pos]; 0310 if (c == QLatin1Char('\\')) { 0311 if ((pos + 1) < len) { 0312 c = pattern[++pos]; 0313 } 0314 } else if (c == QLatin1Char('.')) { 0315 c = QLatin1Char('?'); 0316 if ((pos + 1) < len) { 0317 if (pattern[pos + 1] == QLatin1Char('*')) { 0318 c = pattern[++pos]; 0319 } 0320 } 0321 } 0322 rc.append(c); 0323 ++pos; 0324 } 0325 return rc; 0326 }