File indexing completed on 2024-05-12 16:43:47
0001 /* 0002 SPDX-FileCopyrightText: 2000-2002 Michael Edwardes <mte@users.sourceforge.net> 0003 SPDX-FileCopyrightText: 2000-2002 Javier Campos Morales <javi_c@users.sourceforge.net> 0004 SPDX-FileCopyrightText: 2000-2002 Felix Rodriguez <frodriguez@users.sourceforge.net> 0005 SPDX-FileCopyrightText: 2000-2002 John C <thetacoturtle@users.sourceforge.net> 0006 SPDX-FileCopyrightText: 2000-2002 Thomas Baumgart <ipwizard@users.sourceforge.net> 0007 SPDX-FileCopyrightText: 2000-2002 Kevin Tambascio <ktambascio@users.sourceforge.net> 0008 SPDX-FileCopyrightText: 2017 Łukasz Wojniłowicz <lukasz.wojnilowicz@gmail.com> 0009 SPDX-License-Identifier: GPL-2.0-or-later 0010 */ 0011 0012 #ifndef KHOMEVIEW_P_H 0013 #define KHOMEVIEW_P_H 0014 0015 #include "khomeview.h" 0016 0017 #include <config-kmymoney.h> 0018 0019 // ---------------------------------------------------------------------------- 0020 // QT Includes 0021 0022 #include <QList> 0023 #include <QPixmap> 0024 #include <QTimer> 0025 #include <QBuffer> 0026 #include <QStandardPaths> 0027 #include <QDesktopServices> 0028 #include <QUrlQuery> 0029 #include <QWheelEvent> 0030 #include <QPrintDialog> 0031 #include <QVBoxLayout> 0032 #include <QPrinter> 0033 #ifdef ENABLE_WEBENGINE 0034 #include <QWebEngineView> 0035 #else 0036 #include <KWebView> 0037 #include <QWebFrame> 0038 #endif 0039 0040 // ---------------------------------------------------------------------------- 0041 // KDE Includes 0042 0043 #include <KLocalizedString> 0044 #include <KXmlGuiWindow> 0045 #include <KActionCollection> 0046 #include <KMessageBox> 0047 0048 // ---------------------------------------------------------------------------- 0049 // Project Includes 0050 0051 #include "kmymoneyviewbase_p.h" 0052 #include "mymoneyutils.h" 0053 #include "kmymoneyutils.h" 0054 #include "kwelcomepage.h" 0055 #include "kmymoneysettings.h" 0056 #include "mymoneyfile.h" 0057 #include "mymoneyaccount.h" 0058 #include "mymoneyprice.h" 0059 #include "mymoneyreport.h" 0060 #include "mymoneymoney.h" 0061 #include "mymoneyforecast.h" 0062 #include "mymoneysplit.h" 0063 #include "mymoneytransaction.h" 0064 #include "icons.h" 0065 #include "kmymoneywebpage.h" 0066 #include "mymoneyschedule.h" 0067 #include "mymoneysecurity.h" 0068 #include "mymoneyexception.h" 0069 #include "kmymoneyplugin.h" 0070 #include "mymoneyenums.h" 0071 #include "menuenums.h" 0072 #include "plugins/views/reports/reportsviewenums.h" 0073 0074 #define VIEW_LEDGER "ledger" 0075 #define VIEW_SCHEDULE "schedule" 0076 #define VIEW_WELCOME "welcome" 0077 #define VIEW_HOME "home" 0078 #define VIEW_REPORTS "reports" 0079 0080 using namespace Icons; 0081 using namespace eMyMoney; 0082 0083 /** 0084 * @brief Converts a QPixmap to an data URI scheme 0085 * 0086 * According to RFC 2397 0087 * 0088 * @param pixmap Source to convert 0089 * @return full data URI 0090 */ 0091 QString QPixmapToDataUri(const QPixmap& pixmap) 0092 { 0093 QImage image(pixmap.toImage()); 0094 QByteArray byteArray; 0095 QBuffer buffer(&byteArray); 0096 buffer.open(QIODevice::WriteOnly); 0097 image.save(&buffer, "PNG"); // writes the image in PNG format inside the buffer 0098 return QLatin1String("data:image/png;base64,") + QString(byteArray.toBase64()); 0099 } 0100 0101 bool accountNameLess(const MyMoneyAccount &acc1, const MyMoneyAccount &acc2) 0102 { 0103 return acc1.name().localeAwareCompare(acc2.name()) < 0; 0104 } 0105 0106 class KHomeViewPrivate : public KMyMoneyViewBasePrivate 0107 { 0108 Q_DECLARE_PUBLIC(KHomeView) 0109 0110 public: 0111 explicit KHomeViewPrivate(KHomeView *qq) : 0112 KMyMoneyViewBasePrivate(), 0113 q_ptr(qq), 0114 m_view(nullptr), 0115 m_showAllSchedules(false), 0116 m_needLoad(true), 0117 m_netWorthGraphLastValidSize(400, 300), 0118 m_scrollBarPos(0) 0119 { 0120 } 0121 0122 ~KHomeViewPrivate() { 0123 // if user wants to remember the font size, store it here 0124 if (KMyMoneySettings::rememberZoomFactor() && m_view) { 0125 KMyMoneySettings::setZoomFactor(m_view->zoomFactor()); 0126 KMyMoneySettings::self()->save(); 0127 } 0128 } 0129 0130 /** 0131 * Definition of bitmap used as argument for showAccounts(). 0132 */ 0133 enum paymentTypeE { 0134 Preferred = 1, ///< show preferred accounts 0135 Payment = 2, ///< show payment accounts 0136 }; 0137 0138 void init() 0139 { 0140 Q_Q(KHomeView); 0141 m_needLoad = false; 0142 0143 auto vbox = new QVBoxLayout(q); 0144 q->setLayout(vbox); 0145 vbox->setSpacing(6); 0146 vbox->setMargin(0); 0147 0148 #ifdef ENABLE_WEBENGINE 0149 m_view = new QWebEngineView(q); 0150 #else 0151 m_view = new KWebView(q); 0152 #endif 0153 m_view->installEventFilter(q); 0154 m_view->setPage(new MyQWebEnginePage(m_view)); 0155 0156 vbox->addWidget(m_view); 0157 0158 #ifdef ENABLE_WEBENGINE 0159 q->connect(m_view->page(), &QWebEnginePage::urlChanged, 0160 q, &KHomeView::slotOpenUrl); 0161 #else 0162 m_view->page()->setLinkDelegationPolicy(QWebPage::DelegateAllLinks); 0163 q->connect(m_view->page(), &KWebPage::linkClicked, 0164 q, &KHomeView::slotOpenUrl); 0165 #endif 0166 0167 q->connect(MyMoneyFile::instance(), &MyMoneyFile::dataChanged, q, &KHomeView::refresh); 0168 } 0169 0170 /** 0171 * Print an account and its balance and limit 0172 */ 0173 void showAccountEntry(const MyMoneyAccount& acc, const MyMoneyMoney& value, const MyMoneyMoney& valueToMinBal, const bool showMinBal) 0174 { 0175 MyMoneyFile* file = MyMoneyFile::instance(); 0176 QString tmp; 0177 MyMoneySecurity currency = file->currency(acc.currencyId()); 0178 QString amount; 0179 QString amountToMinBal; 0180 0181 //format amounts 0182 amount = MyMoneyUtils::formatMoney(value, acc, currency); 0183 amount.replace(QChar(' '), " "); 0184 if (showMinBal) { 0185 amountToMinBal = MyMoneyUtils::formatMoney(valueToMinBal, acc, currency); 0186 amountToMinBal.replace(QChar(' '), " "); 0187 } 0188 0189 QString cellStatus, pathOK, pathTODO, pathNotOK; 0190 0191 if (KMyMoneySettings::showBalanceStatusOfOnlineAccounts()) { 0192 //show account's online-status 0193 pathOK = QPixmapToDataUri(Icons::get(Icon::DialogOKApply).pixmap(QSize(16,16))); 0194 pathTODO = QPixmapToDataUri(Icons::get(Icon::MailReceive).pixmap(QSize(16,16))); 0195 pathNotOK = QPixmapToDataUri(Icons::get(Icon::DialogCancel).pixmap(QSize(16,16))); 0196 0197 if (acc.value("lastImportedTransactionDate").isEmpty() || acc.value("lastStatementBalance").isEmpty()) 0198 cellStatus = '-'; 0199 else if (file->hasMatchingOnlineBalance(acc)) { 0200 if (file->hasNewerTransaction(acc.id(), QDate::fromString(acc.value("lastImportedTransactionDate"), Qt::ISODate))) 0201 cellStatus = QString("<img src=\"%1\" border=\"0\">").arg(pathTODO); 0202 else 0203 cellStatus = QString("<img src=\"%1\" border=\"0\">").arg(pathOK); 0204 } else 0205 cellStatus = QString("<img src=\"%1\" border=\"0\">").arg(pathNotOK); 0206 0207 tmp = QString("<td class=\"center\">%1</td>").arg(cellStatus); 0208 } 0209 0210 tmp += QString("<td>") + link(VIEW_LEDGER, QString("?id=%1").arg(acc.id())); 0211 if (acc.isClosed()) { 0212 tmp += QLatin1String("<strike>"); 0213 } 0214 tmp += acc.name().replace("<", "<").replace(">", ">"); 0215 if (acc.isClosed()) { 0216 tmp += QLatin1String("</strike>"); 0217 } 0218 tmp += linkend() + "</td>"; 0219 0220 int countNotMarked = 0, countCleared = 0, countNotReconciled = 0; 0221 QString countStr; 0222 0223 if (KMyMoneySettings::showCountOfUnmarkedTransactions() || KMyMoneySettings::showCountOfNotReconciledTransactions()) 0224 countNotMarked = m_transactionStats[acc.id()][(int)Split::State::NotReconciled]; 0225 0226 if (KMyMoneySettings::showCountOfClearedTransactions() || KMyMoneySettings::showCountOfNotReconciledTransactions()) 0227 countCleared = m_transactionStats[acc.id()][(int)Split::State::Cleared]; 0228 0229 if (KMyMoneySettings::showCountOfNotReconciledTransactions()) 0230 countNotReconciled = countNotMarked + countCleared; 0231 0232 if (KMyMoneySettings::showCountOfUnmarkedTransactions()) { 0233 if (countNotMarked) 0234 countStr = QString("%1").arg(countNotMarked); 0235 else 0236 countStr = '-'; 0237 tmp += QString("<td class=\"center\">%1</td>").arg(countStr); 0238 } 0239 0240 if (KMyMoneySettings::showCountOfClearedTransactions()) { 0241 if (countCleared) 0242 countStr = QString("%1").arg(countCleared); 0243 else 0244 countStr = '-'; 0245 tmp += QString("<td class=\"center\">%1</td>").arg(countStr); 0246 } 0247 0248 if (KMyMoneySettings::showCountOfNotReconciledTransactions()) { 0249 if (countNotReconciled) 0250 countStr = QString("%1").arg(countNotReconciled); 0251 else 0252 countStr = '-'; 0253 tmp += QString("<td class=\"center\">%1</td>").arg(countStr); 0254 } 0255 0256 if (KMyMoneySettings::showDateOfLastReconciliation()) { 0257 const auto lastReconciliationDate = acc.lastReconciliationDate().toString(Qt::SystemLocaleShortDate).replace(QChar(' '), " "); 0258 tmp += QString("<td>%1</d>").arg(lastReconciliationDate); 0259 } 0260 0261 //show account balance 0262 tmp += QString("<td class=\"right\">%1</td>").arg(showColoredAmount(amount, value.isNegative())); 0263 0264 //show minimum balance column if requested 0265 if (showMinBal) { 0266 //if it is an investment, show minimum balance empty 0267 if (acc.accountType() == Account::Type::Investment) { 0268 tmp += QString("<td class=\"right\"> </td>"); 0269 } else { 0270 //show minimum balance entry 0271 tmp += QString("<td class=\"right\">%1</td>").arg(showColoredAmount(amountToMinBal, valueToMinBal.isNegative())); 0272 } 0273 } 0274 // qDebug("accountEntry = '%s'", tmp.toLatin1()); 0275 m_html += tmp; 0276 } 0277 0278 void showAccountEntry(const MyMoneyAccount& acc) 0279 { 0280 const auto file = MyMoneyFile::instance(); 0281 MyMoneyMoney value; 0282 0283 bool showLimit = KMyMoneySettings::showLimitInfo(); 0284 0285 if (acc.accountType() == Account::Type::Investment) { 0286 //investment accounts show the balances of all its subaccounts 0287 value = investmentBalance(acc); 0288 0289 //investment accounts have no minimum balance 0290 showAccountEntry(acc, value, MyMoneyMoney(), showLimit); 0291 } else { 0292 //get balance for normal accounts 0293 value = file->balance(acc.id(), QDate::currentDate()); 0294 if (acc.currencyId() != file->baseCurrency().id()) { 0295 const auto curPrice = file->price(acc.tradingCurrencyId(), file->baseCurrency().id(), QDate::currentDate()); 0296 const auto curRate = curPrice.rate(file->baseCurrency().id()); 0297 auto baseValue = value * curRate; 0298 baseValue = baseValue.convert(file->baseCurrency().smallestAccountFraction()); 0299 m_total += baseValue; 0300 } else { 0301 m_total += value; 0302 } 0303 //if credit card or checkings account, show maximum credit 0304 if (acc.accountType() == Account::Type::CreditCard || 0305 acc.accountType() == Account::Type::Checkings) { 0306 QString maximumCredit = acc.value("maxCreditAbsolute"); 0307 if (maximumCredit.isEmpty()) { 0308 maximumCredit = acc.value("minBalanceAbsolute"); 0309 } 0310 MyMoneyMoney maxCredit = MyMoneyMoney(maximumCredit); 0311 showAccountEntry(acc, value, value - maxCredit, showLimit); 0312 } else { 0313 //otherwise use minimum balance 0314 QString minimumBalance = acc.value("minBalanceAbsolute"); 0315 MyMoneyMoney minBalance = MyMoneyMoney(minimumBalance); 0316 showAccountEntry(acc, value, value - minBalance, showLimit); 0317 } 0318 } 0319 } 0320 0321 /** 0322 * @param acc the investment account 0323 * @return the balance in the currency of the investment account 0324 */ 0325 MyMoneyMoney investmentBalance(const MyMoneyAccount& acc) 0326 { 0327 auto file = MyMoneyFile::instance(); 0328 auto value = file->balance(acc.id(), QDate::currentDate()); 0329 foreach (const auto accountID, acc.accountList()) { 0330 auto stock = file->account(accountID); 0331 if (!stock.isClosed()) { 0332 try { 0333 MyMoneyMoney val; 0334 MyMoneyMoney balance = file->balance(stock.id(), QDate::currentDate()); 0335 MyMoneySecurity security = file->security(stock.currencyId()); 0336 const MyMoneyPrice &price = file->price(stock.currencyId(), security.tradingCurrency()); 0337 val = (balance * price.rate(security.tradingCurrency())).convertPrecision(security.pricePrecision()); 0338 // adjust value of security to the currency of the account 0339 MyMoneySecurity accountCurrency = file->currency(acc.currencyId()); 0340 val = val * file->price(security.tradingCurrency(), accountCurrency.id()).rate(accountCurrency.id()); 0341 val = val.convert(acc.fraction()); 0342 value += val; 0343 } catch (const MyMoneyException &e) { 0344 qWarning("%s", qPrintable(QString("cannot convert stock balance of %1 to base currency: %2").arg(stock.name(), e.what()))); 0345 } 0346 } 0347 } 0348 return value; 0349 } 0350 0351 /** 0352 * Print text in the color set for negative numbers, if @p amount is negative 0353 * abd @p isNegative is true 0354 */ 0355 QString showColoredAmount(const QString& amount, bool isNegative) 0356 { 0357 if (isNegative) { 0358 //if negative, get the settings for negative numbers 0359 return QString("<font color=\"%1\">%2</font>").arg(KMyMoneySettings::schemeColor(SchemeColor::Negative).name(), amount); 0360 } 0361 0362 //if positive, return the same string 0363 return amount; 0364 } 0365 0366 /** 0367 * Run the forecast 0368 */ 0369 void doForecast() 0370 { 0371 //clear m_accountList because forecast is about to changed 0372 m_accountList.clear(); 0373 0374 //reinitialize the object 0375 m_forecast = KMyMoneyUtils::forecast(); 0376 0377 //If forecastDays lower than accountsCycle, adjust to the first cycle 0378 if (m_forecast.accountsCycle() > m_forecast.forecastDays()) 0379 m_forecast.setForecastDays(m_forecast.accountsCycle()); 0380 0381 //Get all accounts of the right type to calculate forecast 0382 m_forecast.doForecast(); 0383 } 0384 0385 /** 0386 * Calculate the forecast balance after a payment has been made 0387 */ 0388 MyMoneyMoney forecastPaymentBalance(const MyMoneyAccount& acc, const MyMoneyMoney& payment, QDate& paymentDate) 0389 { 0390 //if paymentDate before or equal to currentDate set it to current date plus 1 0391 //so we get to accumulate forecast balance correctly 0392 if (paymentDate <= QDate::currentDate()) 0393 paymentDate = QDate::currentDate().addDays(1); 0394 0395 //check if the account is already there 0396 if (m_accountList.find(acc.id()) == m_accountList.end() 0397 || m_accountList[acc.id()].find(paymentDate) == m_accountList[acc.id()].end()) { 0398 if (paymentDate == QDate::currentDate()) { 0399 m_accountList[acc.id()][paymentDate] = m_forecast.forecastBalance(acc, paymentDate); 0400 } else { 0401 m_accountList[acc.id()][paymentDate] = m_forecast.forecastBalance(acc, paymentDate.addDays(-1)); 0402 } 0403 } 0404 m_accountList[acc.id()][paymentDate] = m_accountList[acc.id()][paymentDate] + payment; 0405 return m_accountList[acc.id()][paymentDate]; 0406 } 0407 0408 void loadView() 0409 { 0410 Q_Q(KHomeView); 0411 m_view->setZoomFactor(KMyMoneySettings::zoomFactor()); 0412 0413 QList<MyMoneyAccount> list; 0414 if (MyMoneyFile::instance()->storage()) { 0415 MyMoneyFile::instance()->accountList(list); 0416 } 0417 if (list.isEmpty()) { 0418 m_view->setHtml(KWelcomePage::welcomePage(), QUrl("file://")); 0419 } else { 0420 // preload transaction statistics 0421 m_transactionStats = MyMoneyFile::instance()->countTransactionsWithSpecificReconciliationState(); 0422 0423 // keep current location on page 0424 m_scrollBarPos = 0; 0425 #ifndef ENABLE_WEBENGINE 0426 m_scrollBarPos = m_view->page()->mainFrame()->scrollBarValue(Qt::Vertical); 0427 #endif 0428 0429 //clear the forecast flag so it will be reloaded 0430 m_forecast.setForecastDone(false); 0431 0432 const QString filename = QStandardPaths::locate(QStandardPaths::AppConfigLocation, "html/kmymoney.css"); 0433 QString header = QString("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0//EN\">\n<html><head><link rel=\"stylesheet\" type=\"text/css\" href=\"%1\">\n").arg(QUrl::fromLocalFile(filename).url()); 0434 0435 header += KMyMoneyUtils::variableCSS(); 0436 0437 header += "</head><body id=\"summaryview\">\n"; 0438 0439 QString footer = "</body></html>\n"; 0440 0441 m_html.clear(); 0442 m_html += header; 0443 0444 m_html += QString("<div id=\"summarytitle\">%1</div>").arg(i18n("Your Financial Summary")); 0445 0446 QStringList settings = KMyMoneySettings::listOfItems(); 0447 0448 QStringList::ConstIterator it; 0449 0450 for (it = settings.constBegin(); it != settings.constEnd(); ++it) { 0451 int option = (*it).toInt(); 0452 if (option > 0) { 0453 switch (option) { 0454 case 1: // payments 0455 showPayments(); 0456 break; 0457 0458 case 2: // preferred accounts 0459 showAccounts(Preferred, i18n("Preferred Accounts")); 0460 break; 0461 0462 case 3: // payment accounts 0463 // Check if preferred accounts are shown separately 0464 if (settings.contains("2")) { 0465 showAccounts(static_cast<paymentTypeE>(Payment | Preferred), 0466 i18n("Payment Accounts")); 0467 } else { 0468 showAccounts(Payment, i18n("Payment Accounts")); 0469 } 0470 break; 0471 case 4: // favorite reports 0472 showFavoriteReports(); 0473 break; 0474 case 5: // forecast 0475 showForecast(); 0476 break; 0477 case 6: // net worth graph over all accounts 0478 showNetWorthGraph(); 0479 break; 0480 case 7: // forecast (history) - currently unused 0481 break; 0482 case 8: // assets and liabilities 0483 showAssetsLiabilities(); 0484 break; 0485 case 9: // budget 0486 showBudget(); 0487 break; 0488 case 10: // cash flow summary 0489 showCashFlowSummary(); 0490 break; 0491 } 0492 m_html += "<div class=\"gap\"> </div>\n"; 0493 } 0494 } 0495 0496 m_html += "<div id=\"returnlink\">"; 0497 m_html += link(VIEW_WELCOME, QString()) + i18n("Show KMyMoney welcome page") + linkend(); 0498 m_html += "</div>"; 0499 m_html += "<div id=\"vieweffect\"></div>"; 0500 m_html += footer; 0501 0502 m_view->setHtml(m_html, QUrl("file://")); 0503 0504 #ifndef ENABLE_WEBENGINE 0505 if (m_scrollBarPos) { 0506 QMetaObject::invokeMethod(q, "slotAdjustScrollPos", Qt::QueuedConnection); 0507 } 0508 #endif 0509 } 0510 } 0511 0512 void showNetWorthGraph() 0513 { 0514 Q_Q(KHomeView); 0515 0516 // Adjust the size 0517 QSize netWorthGraphSize = q->size(); 0518 netWorthGraphSize -= QSize(80, 30); 0519 m_netWorthGraphLastValidSize = netWorthGraphSize; 0520 0521 m_html += QString("<div class=\"shadow\"><div class=\"displayblock\"><div class=\"summaryheader\">%1</div>\n<div class=\"gap\"> </div>\n").arg(i18n("Net Worth Forecast")); 0522 m_html += QString("<table width=\"100%\" cellspacing=\"0\" cellpadding=\"2\" class=\"summarytable\" >"); 0523 m_html += QString("<tr>"); 0524 0525 if (const auto reportsPlugin = pPlugins.data.value(QStringLiteral("reportsview"), nullptr)) { 0526 const auto variantReport = reportsPlugin->requestData(QString(), eWidgetPlugin::WidgetType::NetWorthForecast); 0527 if (!variantReport.isNull()) { 0528 auto report = variantReport.value<QWidget *>(); 0529 report->resize(m_netWorthGraphLastValidSize); 0530 m_html += QString("<td><center><img src=\"%1\" ALT=\"Networth\" width=\"100%\" ></center></td>").arg(QPixmapToDataUri(report->grab())); 0531 delete report; 0532 } 0533 } else { 0534 m_html += QString("<td><center>%1</center></td>").arg(i18n("Enable reports plugin to see this chart.")); 0535 } 0536 0537 m_html += QString("</tr>"); 0538 m_html += QString("</table></div></div>"); 0539 } 0540 0541 void showPayments() 0542 { 0543 MyMoneyFile* file = MyMoneyFile::instance(); 0544 QList<MyMoneySchedule> overdues; 0545 QList<MyMoneySchedule> schedule; 0546 int i = 0; 0547 0548 //if forecast has not been executed yet, do it. 0549 if (!m_forecast.isForecastDone()) 0550 doForecast(); 0551 0552 schedule = file->scheduleList(QString(), Schedule::Type::Any, 0553 Schedule::Occurrence::Any, 0554 Schedule::PaymentType::Any, 0555 QDate::currentDate(), 0556 QDate::currentDate().addMonths(1), false); 0557 overdues = file->scheduleList(QString(), Schedule::Type::Any, 0558 Schedule::Occurrence::Any, 0559 Schedule::PaymentType::Any, 0560 QDate(), QDate(), true); 0561 0562 if (schedule.empty() && overdues.empty()) 0563 return; 0564 0565 // HACK 0566 // Remove the finished schedules 0567 QList<MyMoneySchedule>::Iterator d_it; 0568 //regular schedules 0569 d_it = schedule.begin(); 0570 while (d_it != schedule.end()) { 0571 if ((*d_it).isFinished()) { 0572 d_it = schedule.erase(d_it); 0573 continue; 0574 } 0575 ++d_it; 0576 } 0577 //overdue schedules 0578 d_it = overdues.begin(); 0579 while (d_it != overdues.end()) { 0580 if ((*d_it).isFinished()) { 0581 d_it = overdues.erase(d_it); 0582 continue; 0583 } 0584 ++d_it; 0585 } 0586 0587 m_html += "<div class=\"shadow\"><div class=\"displayblock\">"; 0588 m_html += QString("<div class=\"summaryheader\">%1</div>\n").arg(i18n("Payments")); 0589 0590 if (!overdues.isEmpty()) { 0591 m_html += "<div class=\"gap\"> </div>\n"; 0592 0593 qSort(overdues); 0594 QList<MyMoneySchedule>::Iterator it; 0595 QList<MyMoneySchedule>::Iterator it_f; 0596 0597 m_html += "<table width=\"100%\" cellspacing=\"0\" cellpadding=\"2\" class=\"summarytable\" >"; 0598 m_html += QString("<tr class=\"itemtitle warningtitle\" ><td colspan=\"5\">%1</td></tr>\n").arg(showColoredAmount(i18n("Overdue payments"), true)); 0599 m_html += "<tr class=\"item warning\">"; 0600 m_html += "<td class=\"left\" width=\"10%\">"; 0601 m_html += i18n("Date"); 0602 m_html += "</td>"; 0603 m_html += "<td class=\"left\" width=\"40%\">"; 0604 m_html += i18n("Schedule"); 0605 m_html += "</td>"; 0606 m_html += "<td class=\"left\" width=\"20%\">"; 0607 m_html += i18n("Account"); 0608 m_html += "</td>"; 0609 m_html += "<td class=\"right\" width=\"15%\">"; 0610 m_html += i18n("Amount"); 0611 m_html += "</td>"; 0612 m_html += "<td class=\"right\" width=\"15%\">"; 0613 m_html += i18n("Balance after"); 0614 m_html += "</td>"; 0615 m_html += "</tr>"; 0616 0617 for (it = overdues.begin(); it != overdues.end(); ++it) { 0618 // determine number of overdue payments 0619 int cnt = 0620 (*it).transactionsRemainingUntil(QDate::currentDate().addDays(-1)); 0621 0622 m_html += QString("<tr class=\"row-%1\">").arg(i++ & 0x01 ? "even" : "odd"); 0623 showPaymentEntry(*it, cnt); 0624 m_html += "</tr>"; 0625 } 0626 m_html += "</table>"; 0627 } 0628 0629 if (!schedule.isEmpty()) { 0630 qSort(schedule); 0631 0632 // Extract todays payments if any 0633 QList<MyMoneySchedule> todays; 0634 QList<MyMoneySchedule>::Iterator t_it; 0635 for (t_it = schedule.begin(); t_it != schedule.end();) { 0636 if ((*t_it).adjustedNextDueDate() == QDate::currentDate()) { 0637 todays.append(*t_it); 0638 (*t_it).setNextDueDate((*t_it).nextPayment(QDate::currentDate())); 0639 0640 // if adjustedNextDueDate is still currentDate then remove it from 0641 // scheduled payments 0642 if ((*t_it).adjustedNextDueDate() == QDate::currentDate()) { 0643 t_it = schedule.erase(t_it); 0644 continue; 0645 } 0646 } 0647 ++t_it; 0648 } 0649 0650 if (todays.count() > 0) { 0651 m_html += "<div class=\"gap\"> </div>\n"; 0652 m_html += "<table width=\"100%\" cellspacing=\"0\" cellpadding=\"2\" class=\"summarytable\" >"; 0653 m_html += QString("<tr class=\"itemtitle\"><td class=\"left\" colspan=\"5\">%1</td></tr>\n").arg(i18n("Today's due payments")); 0654 m_html += "<tr class=\"item\">"; 0655 m_html += "<td class=\"left\" width=\"10%\">"; 0656 m_html += i18n("Date"); 0657 m_html += "</td>"; 0658 m_html += "<td class=\"left\" width=\"40%\">"; 0659 m_html += i18n("Schedule"); 0660 m_html += "</td>"; 0661 m_html += "<td class=\"left\" width=\"20%\">"; 0662 m_html += i18n("Account"); 0663 m_html += "</td>"; 0664 m_html += "<td class=\"right\" width=\"15%\">"; 0665 m_html += i18n("Amount"); 0666 m_html += "</td>"; 0667 m_html += "<td class=\"right\" width=\"15%\">"; 0668 m_html += i18n("Balance after"); 0669 m_html += "</td>"; 0670 m_html += "</tr>"; 0671 0672 for (t_it = todays.begin(); t_it != todays.end(); ++t_it) { 0673 m_html += QString("<tr class=\"row-%1\">").arg(i++ & 0x01 ? "even" : "odd"); 0674 showPaymentEntry(*t_it); 0675 m_html += "</tr>"; 0676 } 0677 m_html += "</table>"; 0678 } 0679 0680 if (!schedule.isEmpty()) { 0681 m_html += "<div class=\"gap\"> </div>\n"; 0682 0683 QList<MyMoneySchedule>::Iterator it; 0684 0685 m_html += "<table width=\"100%\" cellspacing=\"0\" cellpadding=\"2\" class=\"summarytable\" >"; 0686 m_html += QString("<tr class=\"itemtitle\"><td class=\"left\" colspan=\"5\">%1</td></tr>\n").arg(i18n("Future payments")); 0687 m_html += "<tr class=\"item\">"; 0688 m_html += "<td class=\"left\" width=\"10%\">"; 0689 m_html += i18n("Date"); 0690 m_html += "</td>"; 0691 m_html += "<td class=\"left\" width=\"40%\">"; 0692 m_html += i18n("Schedule"); 0693 m_html += "</td>"; 0694 m_html += "<td class=\"left\" width=\"20%\">"; 0695 m_html += i18n("Account"); 0696 m_html += "</td>"; 0697 m_html += "<td class=\"right\" width=\"15%\">"; 0698 m_html += i18n("Amount"); 0699 m_html += "</td>"; 0700 m_html += "<td class=\"right\" width=\"15%\">"; 0701 m_html += i18n("Balance after"); 0702 m_html += "</td>"; 0703 m_html += "</tr>"; 0704 0705 // show all or the first 6 entries 0706 int cnt; 0707 cnt = (m_showAllSchedules) ? -1 : 6; 0708 bool needMoreLess = m_showAllSchedules; 0709 0710 QDate lastDate = QDate::currentDate().addMonths(1); 0711 qSort(schedule); 0712 do { 0713 it = schedule.begin(); 0714 if (it == schedule.end()) 0715 break; 0716 0717 // if the next due date is invalid (schedule is finished) 0718 // we remove it from the list 0719 QDate nextDate = (*it).nextDueDate(); 0720 if (!nextDate.isValid()) { 0721 schedule.erase(it); 0722 continue; 0723 } 0724 0725 if (nextDate > lastDate) 0726 break; 0727 0728 if (cnt == 0) { 0729 needMoreLess = true; 0730 break; 0731 } 0732 0733 // in case we've shown the current recurrence as overdue, 0734 // we don't show it here again, but keep the schedule 0735 // as it might show up later in the list again 0736 if (!(*it).isOverdue()) { 0737 if (cnt > 0) 0738 --cnt; 0739 0740 m_html += QString("<tr class=\"row-%1\">").arg(i++ & 0x01 ? "even" : "odd"); 0741 showPaymentEntry(*it); 0742 m_html += "</tr>"; 0743 0744 // for single occurrence we have reported everything so we 0745 // better get out of here. 0746 if ((*it).occurrence() == Schedule::Occurrence::Once) { 0747 schedule.erase(it); 0748 continue; 0749 } 0750 } 0751 0752 // if nextPayment returns an invalid date, setNextDueDate will 0753 // just skip it, resulting in a loop 0754 // we check the resulting date and erase the schedule if invalid 0755 if (!((*it).nextPayment((*it).nextDueDate())).isValid()) { 0756 schedule.erase(it); 0757 continue; 0758 } 0759 0760 (*it).setNextDueDate((*it).nextPayment((*it).nextDueDate())); 0761 qSort(schedule); 0762 } while (1); 0763 0764 if (needMoreLess) { 0765 m_html += QString("<tr class=\"row-%1\">").arg(i++ & 0x01 ? "even" : "odd"); 0766 m_html += "<td colspan=\"5\">"; 0767 if (m_showAllSchedules) { 0768 m_html += link(VIEW_SCHEDULE, QString("?mode=%1").arg("reduced")) + i18nc("Less...", "Show fewer schedules on the list") + linkend(); 0769 } else { 0770 m_html += link(VIEW_SCHEDULE, QString("?mode=%1").arg("full")) + i18nc("More...", "Show more schedules on the list") + linkend(); 0771 } 0772 m_html += "</td>"; 0773 m_html += "</tr>"; 0774 } 0775 m_html += "</table>"; 0776 } 0777 } 0778 m_html += "</div></div>"; 0779 } 0780 0781 void showPaymentEntry(const MyMoneySchedule& sched, int cnt = 1) 0782 { 0783 QString tmp; 0784 MyMoneyFile* file = MyMoneyFile::instance(); 0785 0786 try { 0787 MyMoneyAccount acc = sched.account(); 0788 if (!acc.id().isEmpty()) { 0789 MyMoneyTransaction t = sched.transaction(); 0790 // only show the entry, if it is still active 0791 if (!sched.isFinished()) { 0792 MyMoneySplit sp = t.splitByAccount(acc.id(), true); 0793 0794 QString pathEnter = QPixmapToDataUri(Icons::get(Icon::KeyEnter).pixmap(QSize(16,16))); 0795 QString pathSkip = QPixmapToDataUri(Icons::get(Icon::SkipForward).pixmap(QSize(16, 16))); 0796 0797 //show payment date 0798 tmp = QString("<td>") + 0799 QLocale().toString(sched.adjustedNextDueDate(), QLocale::ShortFormat) + 0800 "</td><td>"; 0801 if (!pathEnter.isEmpty()) 0802 tmp += link(VIEW_SCHEDULE, QString("?id=%1&mode=enter").arg(sched.id()), i18n("Enter schedule")) + QString("<img src=\"%1\" border=\"0\"></a>").arg(pathEnter) + linkend(); 0803 if (!pathSkip.isEmpty()) 0804 tmp += " " + link(VIEW_SCHEDULE, QString("?id=%1&mode=skip").arg(sched.id()), i18n("Skip schedule")) + QString("<img src=\"%1\" border=\"0\"></a>").arg(pathSkip) + linkend(); 0805 0806 tmp += QString(" "); 0807 tmp += link(VIEW_SCHEDULE, QString("?id=%1&mode=edit").arg(sched.id()), i18n("Edit schedule")) + sched.name() + linkend(); 0808 0809 //show quantity of payments overdue if any 0810 if (cnt > 1) 0811 tmp += i18np(" (%1 payment)", " (%1 payments)", cnt); 0812 0813 //show account of the main split 0814 tmp += "</td><td>"; 0815 tmp += QString(file->account(acc.id()).name()); 0816 0817 //show amount of the schedule 0818 tmp += "</td><td align=\"right\">"; 0819 0820 const MyMoneySecurity& currency = MyMoneyFile::instance()->currency(acc.currencyId()); 0821 MyMoneyMoney payment = MyMoneyMoney(sp.value(t.commodity(), acc.currencyId()) * cnt); 0822 QString amount = MyMoneyUtils::formatMoney(payment, acc, currency); 0823 amount.replace(QChar(' '), " "); 0824 tmp += showColoredAmount(amount, payment.isNegative()); 0825 tmp += "</td>"; 0826 //show balance after payments 0827 tmp += "<td align=\"right\">"; 0828 QDate paymentDate = QDate(sched.adjustedNextDueDate()); 0829 MyMoneyMoney balanceAfter = forecastPaymentBalance(acc, payment, paymentDate); 0830 QString balance = MyMoneyUtils::formatMoney(balanceAfter, acc, currency); 0831 balance.replace(QChar(' '), " "); 0832 tmp += showColoredAmount(balance, balanceAfter.isNegative()); 0833 tmp += "</td>"; 0834 0835 // qDebug("paymentEntry = '%s'", tmp.toLatin1()); 0836 m_html += tmp; 0837 } 0838 } 0839 } catch (const MyMoneyException &e) { 0840 qDebug("Unable to display schedule entry: %s", e.what()); 0841 } 0842 } 0843 0844 void showAccounts(paymentTypeE type, const QString& header) 0845 { 0846 MyMoneyFile* file = MyMoneyFile::instance(); 0847 int prec = MyMoneyMoney::denomToPrec(file->baseCurrency().smallestAccountFraction()); 0848 QList<MyMoneyAccount> accounts; 0849 0850 const auto showAllAccounts = KMyMoneySettings::showAllAccounts(); 0851 0852 // get list of all accounts 0853 file->accountList(accounts); 0854 for (QList<MyMoneyAccount>::Iterator it = accounts.begin(); it != accounts.end();) { 0855 bool removeAccount = false; 0856 if (!(*it).isClosed() || showAllAccounts) { 0857 switch ((*it).accountType()) { 0858 case Account::Type::Expense: 0859 case Account::Type::Income: 0860 // never show a category account 0861 // Note: This might be different in a future version when 0862 // the homepage also shows category based information 0863 removeAccount = true; 0864 break; 0865 0866 // Asset and Liability accounts are only shown if they 0867 // have the preferred flag set 0868 case Account::Type::Asset: 0869 case Account::Type::Liability: 0870 case Account::Type::Investment: 0871 // if preferred accounts are requested, then keep in list 0872 if ((*it).value("PreferredAccount") != "Yes" 0873 || (type & Preferred) == 0) { 0874 removeAccount = true; 0875 } 0876 break; 0877 0878 // Check payment accounts. If payment and preferred is selected, 0879 // then always show them. If only payment is selected, then 0880 // show only if preferred flag is not set. 0881 case Account::Type::Checkings: 0882 case Account::Type::Savings: 0883 case Account::Type::Cash: 0884 case Account::Type::CreditCard: 0885 switch (type & (Payment | Preferred)) { 0886 case Payment: 0887 if ((*it).value("PreferredAccount") == "Yes") 0888 removeAccount = true; 0889 break; 0890 0891 case Preferred: 0892 if ((*it).value("PreferredAccount") != "Yes") 0893 removeAccount = true; 0894 break; 0895 0896 case Payment | Preferred: 0897 break; 0898 0899 default: 0900 removeAccount = true; 0901 break; 0902 } 0903 break; 0904 0905 // filter all accounts that are not used on homepage views 0906 default: 0907 removeAccount = true; 0908 break; 0909 } 0910 0911 } else if ((*it).isClosed() || (*it).isInvest()) { 0912 // don't show if closed or a stock account 0913 removeAccount = true; 0914 } 0915 0916 if (removeAccount) 0917 it = accounts.erase(it); 0918 else 0919 ++it; 0920 } 0921 0922 if (!accounts.isEmpty()) { 0923 // sort the accounts by name 0924 qStableSort(accounts.begin(), accounts.end(), accountNameLess); 0925 QString tmp; 0926 int i = 0; 0927 tmp = "<div class=\"shadow\"><div class=\"displayblock\"><div class=\"summaryheader\">" + header + "</div>\n<div class=\"gap\"> </div>\n"; 0928 m_html += tmp; 0929 m_html += "<table width=\"100%\" cellspacing=\"0\" cellpadding=\"2\" class=\"summarytable\" >"; 0930 m_html += "<tr class=\"item\">"; 0931 0932 if (KMyMoneySettings::showBalanceStatusOfOnlineAccounts()) { 0933 QString pathStatusHeader = QPixmapToDataUri(Icons::get(Icon::Download).pixmap(QSize(16,16))); 0934 m_html += QString("<td class=\"center\"><img src=\"%1\" border=\"0\"></td>").arg(pathStatusHeader); 0935 } 0936 0937 m_html += "<td class=\"left\" width=\"35%\">"; 0938 m_html += i18n("Account"); 0939 m_html += "</td>"; 0940 0941 if (KMyMoneySettings::showCountOfUnmarkedTransactions()) 0942 m_html += QString("<td class=\"center\">%1</td>").arg(i18nc("Header not marked", "!M")); 0943 0944 if (KMyMoneySettings::showCountOfClearedTransactions()) 0945 m_html += QString("<td class=\"center\">%1</td>").arg(i18nc("Header cleared", "C")); 0946 0947 if (KMyMoneySettings::showCountOfNotReconciledTransactions()) 0948 m_html += QString("<td class=\"center\">%1</td>").arg(i18nc("Header not reconciled", "!R")); 0949 0950 if (KMyMoneySettings::showDateOfLastReconciliation()) 0951 m_html += QString("<td>%1</td>").arg(i18n("Last Reconciled")); 0952 0953 m_html += "<td width=\"25%\" class=\"right\">"; 0954 m_html += i18n("Current Balance"); 0955 m_html += "</td>"; 0956 0957 //only show limit info if user chose to do so 0958 if (KMyMoneySettings::showLimitInfo()) { 0959 m_html += "<td width=\"40%\" class=\"right\">"; 0960 m_html += i18n("To Minimum Balance / Maximum Credit"); 0961 m_html += "</td>"; 0962 } 0963 m_html += "</tr>"; 0964 0965 m_total = 0; 0966 QList<MyMoneyAccount>::const_iterator it_m; 0967 for (it_m = accounts.constBegin(); it_m != accounts.constEnd(); ++it_m) { 0968 m_html += QString("<tr class=\"row-%1\">").arg(i++ & 0x01 ? "even" : "odd"); 0969 showAccountEntry(*it_m); 0970 m_html += "</tr>"; 0971 } 0972 m_html += QString("<tr class=\"row-%1\">").arg(i++ & 0x01 ? "even" : "odd"); 0973 QString amount = m_total.formatMoney(file->baseCurrency().tradingSymbol(), prec); 0974 if (KMyMoneySettings::showBalanceStatusOfOnlineAccounts()) m_html += "<td></td>"; 0975 m_html += QString("<td class=\"right\"><b>%1</b></td>").arg(i18n("Total")); 0976 if (KMyMoneySettings::showCountOfUnmarkedTransactions()) m_html += "<td></td>"; 0977 if (KMyMoneySettings::showCountOfClearedTransactions()) m_html += "<td></td>"; 0978 if (KMyMoneySettings::showCountOfNotReconciledTransactions()) m_html += "<td></td>"; 0979 if (KMyMoneySettings::showDateOfLastReconciliation()) m_html += "<td></td>"; 0980 m_html += QString("<td class=\"right\"><b>%1</b></td></tr>").arg(showColoredAmount(amount, m_total.isNegative())); 0981 m_html += "</table></div></div>"; 0982 } 0983 } 0984 0985 void showFavoriteReports() 0986 { 0987 QList<MyMoneyReport> reports = MyMoneyFile::instance()->reportList(); 0988 0989 if (!reports.isEmpty()) { 0990 qStableSort(reports.begin(), reports.end(), [&](const MyMoneyReport &rep1, const MyMoneyReport &rep2) { 0991 return rep1.name().localeAwareCompare(rep2.name()) < 0; 0992 }); 0993 bool firstTime = 1; 0994 int row = 0; 0995 QList<MyMoneyReport>::const_iterator it_report = reports.constBegin(); 0996 while (it_report != reports.constEnd()) { 0997 if ((*it_report).isFavorite()) { 0998 if (firstTime) { 0999 m_html += QString("<div class=\"shadow\"><div class=\"displayblock\"><div class=\"summaryheader\">%1</div>\n<div class=\"gap\"> </div>\n").arg(i18n("Favorite Reports")); 1000 m_html += "<table width=\"100%\" cellspacing=\"0\" cellpadding=\"2\" class=\"summarytable\" >"; 1001 m_html += "<tr class=\"item\"><td class=\"left\" width=\"40%\">"; 1002 m_html += i18n("Report"); 1003 m_html += "</td><td width=\"60%\" class=\"left\">"; 1004 m_html += i18n("Comment"); 1005 m_html += "</td></tr>"; 1006 firstTime = false; 1007 } 1008 1009 m_html += QString("<tr class=\"row-%1\"><td>%2%3%4</td><td align=\"left\">%5</td></tr>") 1010 .arg(row++ & 0x01 ? "even" : "odd") 1011 .arg(link(VIEW_REPORTS, QString("?id=%1").arg((*it_report).id()))) 1012 .arg((*it_report).name()) 1013 .arg(linkend()) 1014 .arg((*it_report).comment()); 1015 } 1016 1017 ++it_report; 1018 } 1019 if (!firstTime) 1020 m_html += "</table></div></div>"; 1021 } 1022 } 1023 1024 void showForecast() 1025 { 1026 MyMoneyFile* file = MyMoneyFile::instance(); 1027 QList<MyMoneyAccount> accList; 1028 1029 //if forecast has not been executed yet, do it. 1030 if (!m_forecast.isForecastDone()) 1031 doForecast(); 1032 1033 accList = m_forecast.accountList(); 1034 1035 if (accList.count() > 0) { 1036 // sort the accounts by name 1037 qStableSort(accList.begin(), accList.end(), accountNameLess); 1038 auto i = 0; 1039 1040 auto colspan = 1; 1041 //get begin day 1042 auto beginDay = QDate::currentDate().daysTo(m_forecast.beginForecastDate()); 1043 //if begin day is today skip to next cycle 1044 if (beginDay == 0) 1045 beginDay = m_forecast.accountsCycle(); 1046 1047 // Now output header 1048 m_html += QString("<div class=\"shadow\"><div class=\"displayblock\"><div class=\"summaryheader\">%1</div>\n<div class=\"gap\"> </div>\n").arg(i18ncp("Forecast days", "%1 Day Forecast", "%1 Day Forecast", m_forecast.forecastDays())); 1049 m_html += "<table width=\"100%\" cellspacing=\"0\" cellpadding=\"2\" class=\"summarytable\" >"; 1050 m_html += "<tr class=\"item\"><td class=\"left\" width=\"40%\">"; 1051 m_html += i18n("Account"); 1052 m_html += "</td>"; 1053 auto colWidth = 55 / (m_forecast.forecastDays() / m_forecast.accountsCycle()); 1054 for (i = 0; (i*m_forecast.accountsCycle() + beginDay) <= m_forecast.forecastDays(); ++i) { 1055 m_html += QString("<td width=\"%1%\" class=\"right\">").arg(colWidth); 1056 1057 m_html += i18ncp("Forecast days", "%1 day", "%1 days", i * m_forecast.accountsCycle() + beginDay); 1058 m_html += "</td>"; 1059 colspan++; 1060 } 1061 m_html += "</tr>"; 1062 1063 // Now output entries 1064 i = 0; 1065 1066 QList<MyMoneyAccount>::ConstIterator it_account; 1067 for (it_account = accList.constBegin(); it_account != accList.constEnd(); ++it_account) { 1068 //MyMoneyAccount acc = (*it_n); 1069 1070 m_html += QString("<tr class=\"row-%1\">").arg(i++ & 0x01 ? "even" : "odd"); 1071 m_html += QString("<td width=\"40%\">") + 1072 link(VIEW_LEDGER, QString("?id=%1").arg((*it_account).id())) + (*it_account).name() + linkend() + "</td>"; 1073 1074 qint64 dropZero = -1; //account dropped below zero 1075 qint64 dropMinimum = -1; //account dropped below minimum balance 1076 QString minimumBalance = (*it_account).value("minimumBalance"); 1077 MyMoneyMoney minBalance = MyMoneyMoney(minimumBalance); 1078 MyMoneySecurity currency; 1079 MyMoneyMoney forecastBalance; 1080 1081 //change account to deep currency if account is an investment 1082 if ((*it_account).isInvest()) { 1083 MyMoneySecurity underSecurity = file->security((*it_account).currencyId()); 1084 currency = file->security(underSecurity.tradingCurrency()); 1085 } else { 1086 currency = file->security((*it_account).currencyId()); 1087 } 1088 1089 for (auto f = beginDay; f <= m_forecast.forecastDays(); f += m_forecast.accountsCycle()) { 1090 forecastBalance = m_forecast.forecastBalance(*it_account, QDate::currentDate().addDays(f)); 1091 QString amount; 1092 amount = MyMoneyUtils::formatMoney(forecastBalance, *it_account, currency); 1093 amount.replace(QChar(' '), " "); 1094 m_html += QString("<td width=\"%1%\" align=\"right\">").arg(colWidth); 1095 m_html += QString("%1</td>").arg(showColoredAmount(amount, forecastBalance.isNegative())); 1096 } 1097 1098 m_html += "</tr>"; 1099 1100 //Check if the account is going to be below zero or below the minimal balance in the forecast period 1101 1102 //Check if the account is going to be below minimal balance 1103 dropMinimum = m_forecast.daysToMinimumBalance(*it_account); 1104 1105 //Check if the account is going to be below zero in the future 1106 dropZero = m_forecast.daysToZeroBalance(*it_account); 1107 1108 1109 // spit out possible warnings 1110 QString msg; 1111 1112 // if a minimum balance has been specified, an appropriate warning will 1113 // only be shown, if the drop below 0 is on a different day or not present 1114 1115 if (dropMinimum != -1 1116 && !minBalance.isZero() 1117 && (dropMinimum < dropZero 1118 || dropZero == -1)) { 1119 switch (dropMinimum) { 1120 case 0: 1121 msg = i18n("The balance of %1 is below the minimum balance %2 today.", (*it_account).name(), MyMoneyUtils::formatMoney(minBalance, *it_account, currency)); 1122 msg = showColoredAmount(msg, true); 1123 break; 1124 default: 1125 msg = i18np("The balance of %2 will drop below the minimum balance %3 in %1 day.", 1126 "The balance of %2 will drop below the minimum balance %3 in %1 days.", 1127 dropMinimum - 1, (*it_account).name(), MyMoneyUtils::formatMoney(minBalance, *it_account, currency)); 1128 msg = showColoredAmount(msg, true); 1129 break; 1130 } 1131 1132 if (!msg.isEmpty()) { 1133 m_html += QString("<tr class=\"warning\" style=\"font-weight: normal;\" ><td colspan=%2 align=\"center\" >%1</td></tr>").arg(msg).arg(colspan); 1134 } 1135 } 1136 // a drop below zero is always shown 1137 msg.clear(); 1138 switch (dropZero) { 1139 case -1: 1140 break; 1141 case 0: 1142 if ((*it_account).accountGroup() == Account::Type::Asset) { 1143 msg = i18n("The balance of %1 is below %2 today.", (*it_account).name(), MyMoneyUtils::formatMoney(MyMoneyMoney(), *it_account, currency)); 1144 msg = showColoredAmount(msg, true); 1145 break; 1146 } 1147 if ((*it_account).accountGroup() == Account::Type::Liability) { 1148 msg = i18n("The balance of %1 is above %2 today.", (*it_account).name(), MyMoneyUtils::formatMoney(MyMoneyMoney(), *it_account, currency)); 1149 break; 1150 } 1151 break; 1152 default: 1153 if ((*it_account).accountGroup() == Account::Type::Asset) { 1154 msg = i18np("The balance of %2 will drop below %3 in %1 day.", 1155 "The balance of %2 will drop below %3 in %1 days.", 1156 dropZero, (*it_account).name(), MyMoneyUtils::formatMoney(MyMoneyMoney(), *it_account, currency)); 1157 msg = showColoredAmount(msg, true); 1158 break; 1159 } 1160 if ((*it_account).accountGroup() == Account::Type::Liability) { 1161 msg = i18np("The balance of %2 will raise above %3 in %1 day.", 1162 "The balance of %2 will raise above %3 in %1 days.", 1163 dropZero, (*it_account).name(), MyMoneyUtils::formatMoney(MyMoneyMoney(), *it_account, currency)); 1164 break; 1165 } 1166 } 1167 if (!msg.isEmpty()) { 1168 m_html += QString("<tr class=\"warning\"><td colspan=%2 align=\"center\" ><b>%1</b></td></tr>").arg(msg).arg(colspan); 1169 } 1170 } 1171 m_html += "</table></div></div>"; 1172 1173 } 1174 } 1175 1176 QString link(const QString& view, const QString& query, const QString& _title = QString()) const 1177 { 1178 QString titlePart; 1179 QString title(_title); 1180 if (!title.isEmpty()) 1181 titlePart = QString(" title=\"%1\"").arg(title.replace(QLatin1Char(' '), " ")); 1182 1183 return QString("<a href=\"/%1%2\"%3>").arg(view, query, titlePart); 1184 } 1185 1186 QString linkend() const 1187 { 1188 return QStringLiteral("</a>"); 1189 } 1190 1191 void showAssetsLiabilities() 1192 { 1193 QList<MyMoneyAccount> accounts; 1194 QList<MyMoneyAccount>::ConstIterator it; 1195 QList<MyMoneyAccount> assets; 1196 QList<MyMoneyAccount> liabilities; 1197 MyMoneyMoney netAssets; 1198 MyMoneyMoney netLiabilities; 1199 1200 MyMoneyFile* file = MyMoneyFile::instance(); 1201 int prec = MyMoneyMoney::denomToPrec(file->baseCurrency().smallestAccountFraction()); 1202 int i = 0; 1203 1204 // get list of all accounts 1205 file->accountList(accounts); 1206 1207 const auto showAllAccounts = KMyMoneySettings::showAllAccounts(); 1208 1209 for (it = accounts.constBegin(); it != accounts.constEnd();) { 1210 if (!(*it).isClosed() || showAllAccounts) { 1211 switch ((*it).accountType()) { 1212 // group all assets into one list but make sure that investment accounts always show up 1213 case Account::Type::Investment: 1214 assets << *it; 1215 break; 1216 1217 case Account::Type::Checkings: 1218 case Account::Type::Savings: 1219 case Account::Type::Cash: 1220 case Account::Type::Asset: 1221 case Account::Type::AssetLoan: 1222 // list account if it's the last in the hierarchy or has transactions in it 1223 if ((*it).accountList().isEmpty() || (file->transactionCount((*it).id()) > 0)) { 1224 assets << *it; 1225 } 1226 break; 1227 1228 // group the liabilities into the other 1229 case Account::Type::CreditCard: 1230 case Account::Type::Liability: 1231 case Account::Type::Loan: 1232 // list account if it's the last in the hierarchy or has transactions in it 1233 if ((*it).accountList().isEmpty() || (file->transactionCount((*it).id()) > 0)) { 1234 // Add it if we are not hiding zero balance liabilities, or the balance is not zero 1235 const auto value = 1236 MyMoneyFile::instance()->balance((*it).id(), QDate::currentDate()); 1237 if (!(KMyMoneySettings::hideZeroBalanceLiabilities() && value.isZero())) { 1238 liabilities << *it; 1239 } 1240 } 1241 break; 1242 1243 default: 1244 break; 1245 } 1246 } 1247 ++it; 1248 } 1249 1250 //only do it if we have assets or liabilities account 1251 if (assets.count() > 0 || liabilities.count() > 0) { 1252 // sort the accounts by name 1253 qStableSort(assets.begin(), assets.end(), accountNameLess); 1254 qStableSort(liabilities.begin(), liabilities.end(), accountNameLess); 1255 QString statusHeader; 1256 if (KMyMoneySettings::showBalanceStatusOfOnlineAccounts()) { 1257 QString pathStatusHeader; 1258 pathStatusHeader = QPixmapToDataUri(Icons::get(Icon::OnlineJobOutbox).pixmap(QSize(16, 16))); 1259 statusHeader = QString("<img src=\"%1\" border=\"0\">").arg(pathStatusHeader); 1260 } 1261 1262 //print header 1263 m_html += "<div class=\"shadow\"><div class=\"displayblock\"><div class=\"summaryheader\">" + i18n("Assets and Liabilities Summary") + "</div>\n<div class=\"gap\"> </div>\n"; 1264 m_html += "<table width=\"100%\" cellspacing=\"0\" cellpadding=\"2\" class=\"summarytable\" >"; 1265 1266 //column titles 1267 1268 m_html += "<tr class=\"item\">"; 1269 1270 if (KMyMoneySettings::showBalanceStatusOfOnlineAccounts()) { 1271 m_html += "<td class=\"setcolor\">"; 1272 m_html += statusHeader; 1273 m_html += "</td>"; 1274 } 1275 1276 m_html += "<td class=\"left\" width=\"30%\">"; 1277 m_html += i18n("Asset Accounts"); 1278 m_html += "</td>"; 1279 1280 if (KMyMoneySettings::showCountOfUnmarkedTransactions()) 1281 m_html += "<td class=\"setcolor\">!M</td>"; 1282 1283 if (KMyMoneySettings::showCountOfClearedTransactions()) 1284 m_html += "<td class=\"setcolor\">C</td>"; 1285 1286 if (KMyMoneySettings::showCountOfNotReconciledTransactions()) 1287 m_html += "<td class=\"setcolor\">!R</td>"; 1288 1289 if (KMyMoneySettings::showDateOfLastReconciliation()) 1290 m_html += "<td class=\"setcolor\">" + i18n("Last Reconciled") + "</td>"; 1291 1292 m_html += "<td width=\"15%\" class=\"right\">"; 1293 m_html += i18n("Current Balance"); 1294 m_html += "</td>"; 1295 1296 //intermediate row to separate both columns 1297 m_html += "<td width=\"10%\" class=\"setcolor\"></td>"; 1298 1299 if (KMyMoneySettings::showBalanceStatusOfOnlineAccounts()) { 1300 m_html += "<td class=\"setcolor\">"; 1301 m_html += statusHeader; 1302 m_html += "</td>"; 1303 } 1304 1305 m_html += "<td class=\"left\" width=\"30%\">"; 1306 m_html += i18n("Liability Accounts"); 1307 m_html += "</td>"; 1308 1309 if (KMyMoneySettings::showCountOfUnmarkedTransactions()) 1310 m_html += "<td class=\"setcolor\">!M</td>"; 1311 1312 if (KMyMoneySettings::showCountOfClearedTransactions()) 1313 m_html += "<td class=\"setcolor\">C</td>"; 1314 1315 if (KMyMoneySettings::showCountOfNotReconciledTransactions()) 1316 m_html += "<td class=\"setcolor\">!R</td>"; 1317 1318 if (KMyMoneySettings::showDateOfLastReconciliation()) 1319 m_html += "<td class=\"setcolor\">" + i18n("Last Reconciled") + "</td>"; 1320 1321 m_html += "<td width=\"15%\" class=\"right\">"; 1322 m_html += i18n("Current Balance"); 1323 m_html += "</td></tr>"; 1324 1325 QString placeHolder_Status, placeHolder_Counts; 1326 if (KMyMoneySettings::showBalanceStatusOfOnlineAccounts()) placeHolder_Status = "<td></td>"; 1327 if (KMyMoneySettings::showCountOfUnmarkedTransactions()) placeHolder_Counts = "<td></td>"; 1328 if (KMyMoneySettings::showCountOfClearedTransactions()) placeHolder_Counts += "<td></td>"; 1329 if (KMyMoneySettings::showCountOfNotReconciledTransactions()) placeHolder_Counts += "<td></td>"; 1330 if (KMyMoneySettings::showDateOfLastReconciliation()) placeHolder_Counts += "<td></td>"; 1331 1332 //get asset and liability accounts 1333 QList<MyMoneyAccount>::const_iterator asset_it = assets.constBegin(); 1334 QList<MyMoneyAccount>::const_iterator liabilities_it = liabilities.constBegin(); 1335 for (; asset_it != assets.constEnd() || liabilities_it != liabilities.constEnd();) { 1336 m_html += QString("<tr class=\"row-%1\">").arg(i++ & 0x01 ? "even" : "odd"); 1337 //write an asset account if we still have any 1338 if (asset_it != assets.constEnd()) { 1339 MyMoneyMoney value; 1340 //investment accounts consolidate the balance of its subaccounts 1341 if ((*asset_it).accountType() == Account::Type::Investment) { 1342 value = investmentBalance(*asset_it); 1343 } else { 1344 value = MyMoneyFile::instance()->balance((*asset_it).id(), QDate::currentDate()); 1345 } 1346 1347 //calculate balance for foreign currency accounts 1348 if ((*asset_it).currencyId() != file->baseCurrency().id()) { 1349 const auto curPrice = file->price((*asset_it).tradingCurrencyId(), file->baseCurrency().id(), QDate::currentDate()); 1350 const auto curRate = curPrice.rate(file->baseCurrency().id()); 1351 auto baseValue = value * curRate; 1352 baseValue = baseValue.convert(10000); 1353 netAssets += baseValue; 1354 } else { 1355 netAssets += value; 1356 } 1357 //show the account without minimum balance 1358 showAccountEntry(*asset_it, value, MyMoneyMoney(), false); 1359 ++asset_it; 1360 } else { 1361 //write a white space if we don't 1362 m_html += QString("%1<td></td>%2<td></td>").arg(placeHolder_Status).arg(placeHolder_Counts); 1363 } 1364 1365 //leave the intermediate column empty 1366 m_html += "<td class=\"setcolor\"></td>"; 1367 1368 //write a liability account 1369 if (liabilities_it != liabilities.constEnd()) { 1370 MyMoneyMoney value; 1371 value = MyMoneyFile::instance()->balance((*liabilities_it).id(), QDate::currentDate()); 1372 //calculate balance if foreign currency 1373 if ((*liabilities_it).currencyId() != file->baseCurrency().id()) { 1374 const auto curPrice = file->price((*liabilities_it).tradingCurrencyId(), file->baseCurrency().id(), QDate::currentDate()); 1375 const auto curRate = curPrice.rate(file->baseCurrency().id()); 1376 auto baseValue = value * curRate; 1377 baseValue = baseValue.convert(10000); 1378 netLiabilities += baseValue; 1379 } else { 1380 netLiabilities += value; 1381 } 1382 //show the account without minimum balance 1383 showAccountEntry(*liabilities_it, value, MyMoneyMoney(), false); 1384 ++liabilities_it; 1385 } else { 1386 //leave the space empty if we run out of liabilities 1387 m_html += QString("%1<td></td>%2<td></td>").arg(placeHolder_Status).arg(placeHolder_Counts); 1388 } 1389 m_html += "</tr>"; 1390 } 1391 1392 //calculate net worth 1393 MyMoneyMoney netWorth = netAssets + netLiabilities; 1394 1395 //format assets, liabilities and net worth 1396 QString amountAssets = netAssets.formatMoney(file->baseCurrency().tradingSymbol(), prec); 1397 QString amountLiabilities = netLiabilities.formatMoney(file->baseCurrency().tradingSymbol(), prec); 1398 QString amountNetWorth = netWorth.formatMoney(file->baseCurrency().tradingSymbol(), prec); 1399 amountAssets.replace(QChar(' '), " "); 1400 amountLiabilities.replace(QChar(' '), " "); 1401 amountNetWorth.replace(QChar(' '), " "); 1402 1403 m_html += QString("<tr class=\"row-%1\" style=\"font-weight:bold;\">").arg(i++ & 0x01 ? "even" : "odd"); 1404 1405 //print total for assets 1406 m_html += QString("%1<td class=\"left\">%2</td>%3<td align=\"right\">%4</td>").arg(placeHolder_Status).arg(i18n("Total Assets")).arg(placeHolder_Counts).arg(showColoredAmount(amountAssets, netAssets.isNegative())); 1407 1408 //leave the intermediate column empty 1409 m_html += "<td class=\"setcolor\"></td>"; 1410 1411 //print total liabilities 1412 m_html += QString("%1<td class=\"left\">%2</td>%3<td align=\"right\">%4</td>").arg(placeHolder_Status).arg(i18n("Total Liabilities")).arg(placeHolder_Counts).arg(showColoredAmount(amountLiabilities, netLiabilities.isNegative())); 1413 m_html += "</tr>"; 1414 1415 //print net worth 1416 m_html += QString("<tr class=\"row-%1\" style=\"font-weight:bold;\">").arg(i++ & 0x01 ? "even" : "odd"); 1417 1418 m_html += QString("%1<td></td><td></td>%2<td class=\"setcolor\"></td>").arg(placeHolder_Status).arg(placeHolder_Counts); 1419 m_html += QString("%1<td class=\"left\">%2</td>%3<td align=\"right\">%4</td>").arg(placeHolder_Status).arg(i18n("Net Worth")).arg(placeHolder_Counts).arg(showColoredAmount(amountNetWorth, netWorth.isNegative())); 1420 1421 m_html += "</tr>"; 1422 m_html += "</table>"; 1423 m_html += "</div></div>"; 1424 } 1425 } 1426 1427 void showBudget() 1428 { 1429 QVariant variantReport; 1430 1431 if (const auto reportsPlugin = pPlugins.data.value(QStringLiteral("reportsview"), nullptr)) { 1432 variantReport = reportsPlugin->requestData(QString(), eWidgetPlugin::WidgetType::Budget); 1433 } 1434 1435 if (!variantReport.isNull()) { 1436 m_html.append(variantReport.toString()); 1437 } else { 1438 m_html += "<div class=\"shadow\"><div class=\"displayblock\"><div class=\"summaryheader\">" + i18n("Budget") + "</div>\n<div class=\"gap\"> </div>\n"; 1439 m_html += "<table width=\"100%\" cellspacing=\"0\" cellpadding=\"2\" class=\"summarytable\" >"; 1440 m_html += QString("<tr>"); 1441 m_html += QString("<td><center>%1</center></td>").arg(i18n("Enable reports plugin to see this chart.")); 1442 m_html += QString("</tr>"); 1443 m_html += QString("</table></div></div>"); 1444 } 1445 } 1446 1447 void showCashFlowSummary() 1448 { 1449 MyMoneyTransactionFilter filter; 1450 MyMoneyMoney incomeValue; 1451 MyMoneyMoney expenseValue; 1452 1453 MyMoneyFile* file = MyMoneyFile::instance(); 1454 int prec = MyMoneyMoney::denomToPrec(file->baseCurrency().smallestAccountFraction()); 1455 1456 //set start and end of month dates 1457 QDate startOfMonth = QDate(QDate::currentDate().year(), QDate::currentDate().month(), 1); 1458 QDate endOfMonth = QDate(QDate::currentDate().year(), QDate::currentDate().month(), QDate::currentDate().daysInMonth()); 1459 1460 //Add total income and expenses for this month 1461 //get transactions for current month 1462 filter.setDateFilter(startOfMonth, endOfMonth); 1463 filter.setReportAllSplits(false); 1464 1465 QList<MyMoneyTransaction> transactions = file->transactionList(filter); 1466 //if no transaction then skip and print total in zero 1467 if (transactions.size() > 0) { 1468 1469 //get all transactions for this month 1470 foreach (const auto transaction, transactions) { 1471 //get the splits for each transaction 1472 foreach (const auto split, transaction.splits()) { 1473 if (!split.shares().isZero()) { 1474 auto repSplitAcc = file->account(split.accountId()); 1475 1476 //only add if it is an income or expense 1477 if (repSplitAcc.isIncomeExpense()) { 1478 MyMoneyMoney value; 1479 1480 //convert to base currency if necessary 1481 if (repSplitAcc.currencyId() != file->baseCurrency().id()) { 1482 const auto curPrice = file->price(repSplitAcc.tradingCurrencyId(), file->baseCurrency().id(), QDate::currentDate()); 1483 const auto curRate = curPrice.rate(file->baseCurrency().id()); 1484 value = (split.shares() * MyMoneyMoney::MINUS_ONE) * curRate; 1485 value = value.convert(10000); 1486 } else { 1487 value = (split.shares() * MyMoneyMoney::MINUS_ONE); 1488 } 1489 1490 //store depending on account type 1491 if (repSplitAcc.accountType() == Account::Type::Income) { 1492 incomeValue += value; 1493 } else { 1494 expenseValue += value; 1495 } 1496 } 1497 } 1498 } 1499 } 1500 } 1501 1502 //format income and expenses 1503 QString amountIncome = incomeValue.formatMoney(file->baseCurrency().tradingSymbol(), prec); 1504 QString amountExpense = expenseValue.formatMoney(file->baseCurrency().tradingSymbol(), prec); 1505 amountIncome.replace(QChar(' '), " "); 1506 amountExpense.replace(QChar(' '), " "); 1507 1508 //calculate schedules 1509 1510 //Add all schedules for this month 1511 MyMoneyMoney scheduledIncome; 1512 MyMoneyMoney scheduledExpense; 1513 MyMoneyMoney scheduledLiquidTransfer; 1514 MyMoneyMoney scheduledOtherTransfer; 1515 1516 //get overdues and schedules until the end of this month 1517 QList<MyMoneySchedule> schedule = file->scheduleList(QString(), Schedule::Type::Any, 1518 Schedule::Occurrence::Any, 1519 Schedule::PaymentType::Any, 1520 QDate(), 1521 endOfMonth, false); 1522 1523 //Remove the finished schedules 1524 QList<MyMoneySchedule>::Iterator finished_it; 1525 for (finished_it = schedule.begin(); finished_it != schedule.end();) { 1526 if ((*finished_it).isFinished()) { 1527 finished_it = schedule.erase(finished_it); 1528 continue; 1529 } 1530 ++finished_it; 1531 } 1532 1533 //add income and expenses 1534 QList<MyMoneySchedule>::Iterator sched_it; 1535 for (sched_it = schedule.begin(); sched_it != schedule.end();) { 1536 QDate nextDate = (*sched_it).nextDueDate(); 1537 int cnt = 0; 1538 1539 while (nextDate.isValid() && nextDate <= endOfMonth) { 1540 ++cnt; 1541 nextDate = (*sched_it).nextPayment(nextDate); 1542 // for single occurrence nextDate will not change, so we 1543 // better get out of here. 1544 if ((*sched_it).occurrence() == Schedule::Occurrence::Once) 1545 break; 1546 } 1547 1548 MyMoneyAccount acc = (*sched_it).account(); 1549 if (!acc.id().isEmpty()) { 1550 MyMoneyTransaction transaction = (*sched_it).transaction(); 1551 // only show the entry, if it is still active 1552 1553 MyMoneySplit sp = transaction.splitByAccount(acc.id(), true); 1554 1555 // take care of the autoCalc stuff 1556 if ((*sched_it).type() == Schedule::Type::LoanPayment) { 1557 nextDate = (*sched_it).nextPayment((*sched_it).lastPayment()); 1558 1559 //make sure we have all 'starting balances' so that the autocalc works 1560 QMap<QString, MyMoneyMoney> balanceMap; 1561 1562 foreach (const auto split, transaction.splits()) { 1563 acc = file->account(split.accountId()); 1564 // collect all overdues on the first day 1565 QDate schedDate = nextDate; 1566 if (QDate::currentDate() >= nextDate) 1567 schedDate = QDate::currentDate().addDays(1); 1568 1569 balanceMap[acc.id()] += file->balance(acc.id(), QDate::currentDate()); 1570 } 1571 KMyMoneyUtils::calculateAutoLoan(*sched_it, transaction, balanceMap); 1572 } 1573 1574 //go through the splits and assign to liquid or other transfers 1575 const QList<MyMoneySplit> splits = transaction.splits(); 1576 QList<MyMoneySplit>::const_iterator split_it; 1577 for (split_it = splits.constBegin(); split_it != splits.constEnd(); ++split_it) { 1578 if ((*split_it).accountId() != acc.id()) { 1579 auto repSplitAcc = file->account((*split_it).accountId()); 1580 1581 //get the shares and multiply by the quantity of occurrences in the period 1582 MyMoneyMoney value = (*split_it).shares() * cnt; 1583 1584 //convert to foreign currency if needed 1585 if (repSplitAcc.currencyId() != file->baseCurrency().id()) { 1586 const auto curPrice = file->price(repSplitAcc.tradingCurrencyId(), file->baseCurrency().id(), QDate::currentDate()); 1587 const auto curRate = curPrice.rate(file->baseCurrency().id()); 1588 value = value * curRate; 1589 value = value.convert(10000); 1590 } 1591 1592 if ((repSplitAcc.isLiquidLiability() 1593 || repSplitAcc.isLiquidAsset()) 1594 && acc.accountGroup() != repSplitAcc.accountGroup()) { 1595 scheduledLiquidTransfer += value; 1596 } else if (repSplitAcc.isAssetLiability() 1597 && !repSplitAcc.isLiquidLiability() 1598 && !repSplitAcc.isLiquidAsset()) { 1599 scheduledOtherTransfer += value; 1600 } else if (repSplitAcc.isIncomeExpense()) { 1601 //income and expenses are stored as negative values 1602 if (repSplitAcc.accountType() == Account::Type::Income) 1603 scheduledIncome -= value; 1604 if (repSplitAcc.accountType() == Account::Type::Expense) 1605 scheduledExpense -= value; 1606 } 1607 } 1608 } 1609 } 1610 ++sched_it; 1611 } 1612 1613 //format the currency strings 1614 QString amountScheduledIncome = scheduledIncome.formatMoney(file->baseCurrency().tradingSymbol(), prec); 1615 QString amountScheduledExpense = scheduledExpense.formatMoney(file->baseCurrency().tradingSymbol(), prec); 1616 QString amountScheduledLiquidTransfer = scheduledLiquidTransfer.formatMoney(file->baseCurrency().tradingSymbol(), prec); 1617 QString amountScheduledOtherTransfer = scheduledOtherTransfer.formatMoney(file->baseCurrency().tradingSymbol(), prec); 1618 1619 amountScheduledIncome.replace(QChar(' '), " "); 1620 amountScheduledExpense.replace(QChar(' '), " "); 1621 amountScheduledLiquidTransfer.replace(QChar(' '), " "); 1622 amountScheduledOtherTransfer.replace(QChar(' '), " "); 1623 1624 //get liquid assets and liabilities 1625 QList<MyMoneyAccount> accounts; 1626 QList<MyMoneyAccount>::const_iterator account_it; 1627 MyMoneyMoney liquidAssets; 1628 MyMoneyMoney liquidLiabilities; 1629 1630 // get list of all accounts 1631 file->accountList(accounts); 1632 for (account_it = accounts.constBegin(); account_it != accounts.constEnd();) { 1633 if (!(*account_it).isClosed()) { 1634 switch ((*account_it).accountType()) { 1635 //group all assets into one list 1636 case Account::Type::Checkings: 1637 case Account::Type::Savings: 1638 case Account::Type::Cash: { 1639 MyMoneyMoney value = MyMoneyFile::instance()->balance((*account_it).id(), QDate::currentDate()); 1640 //calculate balance for foreign currency accounts 1641 if ((*account_it).currencyId() != file->baseCurrency().id()) { 1642 const auto curPrice = file->price((*account_it).tradingCurrencyId(), file->baseCurrency().id(), QDate::currentDate()); 1643 const auto curRate = curPrice.rate(file->baseCurrency().id()); 1644 auto baseValue = value * curRate; 1645 liquidAssets += baseValue; 1646 liquidAssets = liquidAssets.convert(10000); 1647 } else { 1648 liquidAssets += value; 1649 } 1650 break; 1651 } 1652 //group the liabilities into the other 1653 case Account::Type::CreditCard: { 1654 MyMoneyMoney value; 1655 value = MyMoneyFile::instance()->balance((*account_it).id(), QDate::currentDate()); 1656 //calculate balance if foreign currency 1657 if ((*account_it).currencyId() != file->baseCurrency().id()) { 1658 const auto curPrice = file->price((*account_it).tradingCurrencyId(), file->baseCurrency().id(), QDate::currentDate()); 1659 const auto curRate = curPrice.rate(file->baseCurrency().id()); 1660 auto baseValue = value * curRate; 1661 liquidLiabilities += baseValue; 1662 liquidLiabilities = liquidLiabilities.convert(10000); 1663 } else { 1664 liquidLiabilities += value; 1665 } 1666 break; 1667 } 1668 default: 1669 break; 1670 } 1671 } 1672 ++account_it; 1673 } 1674 //calculate net worth 1675 MyMoneyMoney liquidWorth = liquidAssets + liquidLiabilities; 1676 1677 //format assets, liabilities and net worth 1678 QString amountLiquidAssets = liquidAssets.formatMoney(file->baseCurrency().tradingSymbol(), prec); 1679 QString amountLiquidLiabilities = liquidLiabilities.formatMoney(file->baseCurrency().tradingSymbol(), prec); 1680 QString amountLiquidWorth = liquidWorth.formatMoney(file->baseCurrency().tradingSymbol(), prec); 1681 amountLiquidAssets.replace(QChar(' '), " "); 1682 amountLiquidLiabilities.replace(QChar(' '), " "); 1683 amountLiquidWorth.replace(QChar(' '), " "); 1684 1685 //show the summary 1686 m_html += "<div class=\"shadow\"><div class=\"displayblock\"><div class=\"summaryheader\">" + i18n("Cash Flow Summary") + "</div>\n<div class=\"gap\"> </div>\n"; 1687 1688 //print header 1689 m_html += "<table width=\"100%\" cellspacing=\"0\" cellpadding=\"2\" class=\"summarytable\" >"; 1690 //income and expense title 1691 m_html += "<tr class=\"itemtitle\">"; 1692 m_html += "<td class=\"left\" colspan=\"4\">"; 1693 m_html += i18n("Income and Expenses of Current Month"); 1694 m_html += "</td></tr>"; 1695 //column titles 1696 m_html += "<tr class=\"item\">"; 1697 m_html += "<td width=\"25%\" class=\"center\">"; 1698 m_html += i18n("Income"); 1699 m_html += "</td>"; 1700 m_html += "<td width=\"25%\" class=\"center\">"; 1701 m_html += i18n("Scheduled Income"); 1702 m_html += "</td>"; 1703 m_html += "<td width=\"25%\" class=\"center\">"; 1704 m_html += i18n("Expenses"); 1705 m_html += "</td>"; 1706 m_html += "<td width=\"25%\" class=\"center\">"; 1707 m_html += i18n("Scheduled Expenses"); 1708 m_html += "</td>"; 1709 m_html += "</tr>"; 1710 1711 //add row with banding 1712 m_html += QString("<tr class=\"row-even\" style=\"font-weight:bold;\">"); 1713 1714 //print current income 1715 m_html += QString("<td align=\"right\">%2</td>").arg(showColoredAmount(amountIncome, incomeValue.isNegative())); 1716 1717 //print the scheduled income 1718 m_html += QString("<td align=\"right\">%2</td>").arg(showColoredAmount(amountScheduledIncome, scheduledIncome.isNegative())); 1719 1720 //print current expenses 1721 m_html += QString("<td align=\"right\">%2</td>").arg(showColoredAmount(amountExpense, expenseValue.isNegative())); 1722 1723 //print the scheduled expenses 1724 m_html += QString("<td align=\"right\">%2</td>").arg(showColoredAmount(amountScheduledExpense, scheduledExpense.isNegative())); 1725 m_html += "</tr>"; 1726 1727 m_html += "</table>"; 1728 1729 //print header of assets and liabilities 1730 m_html += "<div class=\"gap\"> </div>\n"; 1731 m_html += "<table width=\"100%\" cellspacing=\"0\" cellpadding=\"2\" class=\"summarytable\" >"; 1732 //assets and liabilities title 1733 m_html += "<tr class=\"itemtitle\">"; 1734 m_html += "<td class=\"left\" colspan=\"4\">"; 1735 m_html += i18n("Liquid Assets and Liabilities"); 1736 m_html += "</td></tr>"; 1737 //column titles 1738 m_html += "<tr class=\"item\">"; 1739 m_html += "<td width=\"25%\" class=\"center\">"; 1740 m_html += i18n("Liquid Assets"); 1741 m_html += "</td>"; 1742 m_html += "<td width=\"25%\" class=\"center\">"; 1743 m_html += i18n("Transfers to Liquid Liabilities"); 1744 m_html += "</td>"; 1745 m_html += "<td width=\"25%\" class=\"center\">"; 1746 m_html += i18n("Liquid Liabilities"); 1747 m_html += "</td>"; 1748 m_html += "<td width=\"25%\" class=\"center\">"; 1749 m_html += i18n("Other Transfers"); 1750 m_html += "</td>"; 1751 m_html += "</tr>"; 1752 1753 //add row with banding 1754 m_html += QString("<tr class=\"row-even\" style=\"font-weight:bold;\">"); 1755 1756 //print current liquid assets 1757 m_html += QString("<td align=\"right\">%2</td>").arg(showColoredAmount(amountLiquidAssets, liquidAssets.isNegative())); 1758 1759 //print the scheduled transfers 1760 m_html += QString("<td align=\"right\">%2</td>").arg(showColoredAmount(amountScheduledLiquidTransfer, scheduledLiquidTransfer.isNegative())); 1761 1762 //print current liabilities 1763 m_html += QString("<td align=\"right\">%2</td>").arg(showColoredAmount(amountLiquidLiabilities, liquidLiabilities.isNegative())); 1764 1765 //print the scheduled transfers 1766 m_html += QString("<td align=\"right\">%2</td>").arg(showColoredAmount(amountScheduledOtherTransfer, scheduledOtherTransfer.isNegative())); 1767 1768 1769 m_html += "</tr>"; 1770 1771 m_html += "</table>"; 1772 1773 //final conclusion 1774 MyMoneyMoney profitValue = incomeValue + expenseValue + scheduledIncome + scheduledExpense; 1775 MyMoneyMoney expectedAsset = liquidAssets + scheduledIncome + scheduledExpense + scheduledLiquidTransfer + scheduledOtherTransfer; 1776 MyMoneyMoney expectedLiabilities = liquidLiabilities + scheduledLiquidTransfer; 1777 1778 QString amountExpectedAsset = expectedAsset.formatMoney(file->baseCurrency().tradingSymbol(), prec); 1779 QString amountExpectedLiabilities = expectedLiabilities.formatMoney(file->baseCurrency().tradingSymbol(), prec); 1780 QString amountProfit = profitValue.formatMoney(file->baseCurrency().tradingSymbol(), prec); 1781 amountProfit.replace(QChar(' '), " "); 1782 amountExpectedAsset.replace(QChar(' '), " "); 1783 amountExpectedLiabilities.replace(QChar(' '), " "); 1784 1785 1786 1787 //print header of cash flow status 1788 m_html += "<div class=\"gap\"> </div>\n"; 1789 m_html += "<table width=\"100%\" cellspacing=\"0\" cellpadding=\"2\" class=\"summarytable\" >"; 1790 //income and expense title 1791 m_html += "<tr class=\"itemtitle\">"; 1792 m_html += "<td class=\"left\" colspan=\"4\">"; 1793 m_html += i18n("Cash Flow Status"); 1794 m_html += "</td></tr>"; 1795 //column titles 1796 m_html += "<tr class=\"item\">"; 1797 m_html += "<td> </td>"; 1798 m_html += "<td width=\"25%\" class=\"center\">"; 1799 m_html += i18n("Expected Liquid Assets"); 1800 m_html += "</td>"; 1801 m_html += "<td width=\"25%\" class=\"center\">"; 1802 m_html += i18n("Expected Liquid Liabilities"); 1803 m_html += "</td>"; 1804 m_html += "<td width=\"25%\" class=\"center\">"; 1805 m_html += i18n("Expected Profit/Loss"); 1806 m_html += "</td>"; 1807 m_html += "</tr>"; 1808 1809 //add row with banding 1810 m_html += QString("<tr class=\"row-even\" style=\"font-weight:bold;\">"); 1811 m_html += "<td> </td>"; 1812 1813 //print expected assets 1814 m_html += QString("<td align=\"right\">%2</td>").arg(showColoredAmount(amountExpectedAsset, expectedAsset.isNegative())); 1815 1816 //print expected liabilities 1817 m_html += QString("<td align=\"right\">%2</td>").arg(showColoredAmount(amountExpectedLiabilities, expectedLiabilities.isNegative())); 1818 1819 //print expected profit 1820 m_html += QString("<td align=\"right\">%2</td>").arg(showColoredAmount(amountProfit, profitValue.isNegative())); 1821 1822 m_html += "</tr>"; 1823 m_html += "</table>"; 1824 m_html += "</div></div>"; 1825 } 1826 1827 1828 KHomeView *q_ptr; 1829 1830 /** 1831 * daily balances of an account 1832 */ 1833 typedef QMap<QDate, MyMoneyMoney> dailyBalances; 1834 1835 #ifdef ENABLE_WEBENGINE 1836 QWebEngineView *m_view; 1837 #else 1838 KWebView *m_view; 1839 #endif 1840 1841 QString m_html; 1842 bool m_showAllSchedules; 1843 bool m_needLoad; 1844 MyMoneyForecast m_forecast; 1845 MyMoneyMoney m_total; 1846 /** 1847 * Hold the last valid size of the net worth graph 1848 * for the times when the needed size can't be computed. 1849 */ 1850 QSize m_netWorthGraphLastValidSize; 1851 1852 QMap< QString, QVector<int> > m_transactionStats; 1853 1854 /** 1855 * daily forecast balance of accounts 1856 */ 1857 QMap<QString, dailyBalances> m_accountList; 1858 int m_scrollBarPos; 1859 }; 1860 1861 #endif