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(' '), "&nbsp;");
0184         if (showMinBal) {
0185             amountToMinBal = MyMoneyUtils::formatMoney(valueToMinBal, acc, currency);
0186             amountToMinBal.replace(QChar(' '), "&nbsp;");
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("<", "&lt;").replace(">", "&gt;");
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(' '), "&nbsp;");
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\">&nbsp;</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\">&nbsp;</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\">&nbsp;</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\">&nbsp;</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\">&nbsp;</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\">&nbsp;</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&amp;mode=enter").arg(sched.id()), i18n("Enter schedule")) + QString("<img src=\"%1\" border=\"0\"></a>").arg(pathEnter) + linkend();
0803                     if (!pathSkip.isEmpty())
0804                         tmp += "&nbsp;" + link(VIEW_SCHEDULE, QString("?id=%1&amp;mode=skip").arg(sched.id()), i18n("Skip schedule")) + QString("<img src=\"%1\" border=\"0\"></a>").arg(pathSkip) + linkend();
0805 
0806                     tmp += QString("&nbsp;");
0807                     tmp += link(VIEW_SCHEDULE, QString("?id=%1&amp;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(' '), "&nbsp;");
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(' '), "&nbsp;");
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\">&nbsp;</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\">&nbsp;</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\">&nbsp;</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(' '), "&nbsp;");
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(' '), "&nbsp;"));
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\">&nbsp;</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(' '), "&nbsp;");
1400             amountLiabilities.replace(QChar(' '), "&nbsp;");
1401             amountNetWorth.replace(QChar(' '), "&nbsp;");
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\">&nbsp;</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(' '), "&nbsp;");
1506         amountExpense.replace(QChar(' '), "&nbsp;");
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(' '), "&nbsp;");
1620         amountScheduledExpense.replace(QChar(' '), "&nbsp;");
1621         amountScheduledLiquidTransfer.replace(QChar(' '), "&nbsp;");
1622         amountScheduledOtherTransfer.replace(QChar(' '), "&nbsp;");
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(' '), "&nbsp;");
1682         amountLiquidLiabilities.replace(QChar(' '), "&nbsp;");
1683         amountLiquidWorth.replace(QChar(' '), "&nbsp;");
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\">&nbsp;</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\">&nbsp;</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(' '), "&nbsp;");
1782         amountExpectedAsset.replace(QChar(' '), "&nbsp;");
1783         amountExpectedLiabilities.replace(QChar(' '), "&nbsp;");
1784 
1785 
1786 
1787         //print header of cash flow status
1788         m_html += "<div class=\"gap\">&nbsp;</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>&nbsp;</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>&nbsp;</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