diff options
Diffstat (limited to 'kmymoney2/views/khomeview.cpp')
-rw-r--r-- | kmymoney2/views/khomeview.cpp | 1940 |
1 files changed, 1940 insertions, 0 deletions
diff --git a/kmymoney2/views/khomeview.cpp b/kmymoney2/views/khomeview.cpp new file mode 100644 index 0000000..95b7323 --- /dev/null +++ b/kmymoney2/views/khomeview.cpp @@ -0,0 +1,1940 @@ +/*************************************************************************** + khomeview.cpp - description + ------------------- + begin : Tue Jan 22 2002 + copyright : (C) 2000-2002 by Michael Edwardes + email : [email protected] + Javier Campos Morales <[email protected]> + Felix Rodriguez <[email protected]> + John C <[email protected]> + Thomas Baumgart <[email protected]> + Kevin Tambascio <[email protected]> + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +// ---------------------------------------------------------------------------- +// QT Includes + +#include <qlayout.h> +#include <qdatetime.h> +#include <qapplication.h> +#include <dom/dom_element.h> +#include <dom/dom_doc.h> +#include <dom/dom_text.h> +#include <qfile.h> +#include <qtextstream.h> +#include <qtimer.h> +#include <qbuffer.h> + +// ---------------------------------------------------------------------------- +// KDE Includes + +#include <kglobal.h> +#include <klocale.h> +#include <kstandarddirs.h> +#include <khtmlview.h> +#include <kconfig.h> +#include <kstdaction.h> +#include <kmainwindow.h> +#include <kactioncollection.h> +#include <kapplication.h> +#include <kmessagebox.h> +#include <kdebug.h> +#include <kmdcodec.h> +#include <kglobalsettings.h> +#include <kiconloader.h> + +// ---------------------------------------------------------------------------- +// Project Includes +#include "khomeview.h" +#include "../kmymoneyutils.h" +#include "../kmymoneyglobalsettings.h" +#include "../mymoney/mymoneyfile.h" +#include "../mymoney/mymoneyforecast.h" +#include "../kmymoney2.h" +#include "../reports/kreportchartview.h" +#include "../reports/pivottable.h" +#include "../reports/pivotgrid.h" +#include "../reports/reportaccount.h" +#include "../kmymoneyglobalsettings.h" + + +#define VIEW_LEDGER "ledger" +#define VIEW_SCHEDULE "schedule" +#define VIEW_WELCOME "welcome" +#define VIEW_HOME "home" +#define VIEW_REPORTS "reports" + +// in KOffice version < 1.5 KDCHART_PROPSET_NORMAL_DATA was a static const +// but in 1.5 this has been changed into a #define'd value. So we have to +// make sure, we use the right one. +#ifndef KDCHART_PROPSET_NORMAL_DATA +#define KMM_KDCHART_PROPSET_NORMAL_DATA KDChartParams::KDCHART_PROPSET_NORMAL_DATA +#else +#define KMM_KDCHART_PROPSET_NORMAL_DATA KDCHART_PROPSET_NORMAL_DATA +#endif + +using namespace reports; + +class KHomeView::Private +{ + public: + Private() {} + void addNameIndex(QMap<QString, MyMoneyAccount> &idx, const MyMoneyAccount& account); +}; + +void KHomeView::Private::addNameIndex(QMap<QString, MyMoneyAccount> &idx, const MyMoneyAccount& account) +{ + QString key = account.name(); + + if(idx[key].id().isEmpty()) { + idx[key] = account; + //take care of accounts with duplicate names + } else if(idx[key].id() != account.id()) { + key = account.name() + "[%1]"; + int dup = 2; + while(!idx[key.arg(dup)].id().isEmpty() + && idx[key.arg(dup)].id() != account.id()) + ++dup; + idx[key.arg(dup)] = account; + } +} + +KHomeView::KHomeView(QWidget *parent, const char *name ) : + KMyMoneyViewBase(parent, name, i18n("Home")), + d(new Private), + m_showAllSchedules(false), + m_needReload(true) +{ + m_part = new KHTMLPart(this, "htmlpart_km2"); + addWidget(m_part->view()); + + m_filename = KMyMoneyUtils::findResource("appdata", QString("html/home%1.html")); + +// m_part->openURL(m_filename); + connect(m_part->browserExtension(), SIGNAL(openURLRequest(const KURL&, const KParts::URLArgs&)), + this, SLOT(slotOpenURL(const KURL&, const KParts::URLArgs&))); + + connect(MyMoneyFile::instance(), SIGNAL(dataChanged()), this, SLOT(slotLoadView())); +} + +KHomeView::~KHomeView() +{ + // if user wants to remember the font size, store it here + if (KMyMoneyGlobalSettings::rememberFontSize()) + { + KMyMoneyGlobalSettings::setFontSizePercentage(m_part->zoomFactor()); + //kdDebug() << "Storing font size: " << m_part->zoomFactor() << endl; + KMyMoneyGlobalSettings::self()->writeConfig(); + } + delete d; +} + +void KHomeView::slotLoadView(void) +{ + m_needReload = true; + if(isVisible()) { + loadView(); + m_needReload = false; + } +} + +void KHomeView::show(void) +{ + if(m_needReload) { + loadView(); + m_needReload = false; + } + QWidget::show(); +} + +void KHomeView::slotPrintView(void) +{ + if(m_part && m_part->view()) + m_part->view()->print(); +} + +void KHomeView::loadView(void) +{ + m_part->setZoomFactor( KMyMoneyGlobalSettings::fontSizePercentage() ); + //kdDebug() << "Setting font size: " << m_part->zoomFactor() << endl; + + QValueList<MyMoneyAccount> list; + MyMoneyFile::instance()->accountList(list); + if(list.count() == 0) + { + m_part->openURL(m_filename); + +#if 0 + // (ace) I am experimenting with replacing links in the + // html depending on the state of the engine. It's not + // working. That's why it's #if0'd out. + + DOM::Element e = m_part->document().getElementById("test"); + if ( e.isNull() ) + { + qDebug("Element id=test not found"); + } + else + { + qDebug("Element id=test found!"); + QString tagname = e.tagName().string(); + qDebug("%s",tagname.latin1()); + qDebug("%s id=%s",e.tagName().string().latin1(),e.getAttribute("id").string().latin1()); + + // Find the character data node + DOM::Node n = e.firstChild(); + while (!n.isNull()) + { + qDebug("Child type %u",static_cast<unsigned>(n.nodeType())); + if ( n.nodeType() == DOM::Node::TEXT_NODE ) + { + DOM::Text t = n; + t.setData("Success!!"); + e.replaceChild(n,t); + m_part->document().setDesignMode(true); + m_part->document().importNode(e,true); + m_part->document().updateRendering(); + + qDebug("Data is now %s",t.data().string().latin1()); + } + n = n.nextSibling(); + } + } +#endif + } else { + //clear the forecast flag so it will be reloaded + m_forecast.setForecastDone(false); + + QString filename = KGlobal::dirs()->findResource("appdata", "html/kmymoney2.css"); + 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(filename); + + header += KMyMoneyUtils::variableCSS(); + + header += "</head><body id=\"summaryview\">\n"; + + QString footer = "</body></html>\n"; + + m_part->begin(); + m_part->write(header); + + m_part->write(QString("<div id=\"summarytitle\">%1</div>").arg(i18n("Your Financial Summary"))); + + QStringList settings = KMyMoneyGlobalSettings::itemList(); + + QStringList::ConstIterator it; + + for(it = settings.begin(); it != settings.end(); ++it) { + int option = (*it).toInt(); + if(option > 0) { + switch(option) { + case 1: // payments + showPayments(); + break; + + case 2: // preferred accounts + showAccounts(Preferred, i18n("Preferred Accounts")); + break; + + case 3: // payment accounts + // Check if preferred accounts are shown separately + if(settings.find("2") == settings.end()) { + showAccounts(static_cast<paymentTypeE> (Payment | Preferred), + i18n("Payment Accounts")); + } else { + showAccounts(Payment, i18n("Payment Accounts")); + } + break; + case 4: // favorite reports + showFavoriteReports(); + break; + case 5: // forecast + showForecast(); + break; + case 6: // net worth graph over all accounts + showNetWorthGraph(); + break; + case 8: // assets and liabilities + showAssetsLiabilities(); + break; + case 9: // budget + showBudget(); + break; + case 10: // cash flow summary + showCashFlowSummary(); + break; + + + } + m_part->write("<div class=\"gap\"> </div>\n"); + } + } + + m_part->write("<div id=\"returnlink\">"); + m_part->write(link(VIEW_WELCOME, QString()) + i18n("Show KMyMoney welcome page") + linkend()); + m_part->write("</div>"); + m_part->write("<div id=\"vieweffect\"></div>"); + m_part->write(footer); + m_part->end(); + + } +} + +void KHomeView::showNetWorthGraph(void) +{ +#ifdef HAVE_KDCHART + m_part->write(QString("<div class=\"shadow\"><div class=\"displayblock\"><div class=\"summaryheader\">%1</div>\n<div class=\"gap\"> </div>\n").arg(i18n("Networth Forecast"))); + + MyMoneyReport reportCfg = MyMoneyReport( + MyMoneyReport::eAssetLiability, + MyMoneyReport::eMonths, + MyMoneyTransactionFilter::userDefined, // overridden by the setDateFilter() call below + MyMoneyReport::eDetailTotal, + i18n("Networth Forecast"), + i18n("Generated Report")); + + reportCfg.setChartByDefault(true); + reportCfg.setChartGridLines(false); + reportCfg.setChartDataLabels(false); + reportCfg.setChartType(MyMoneyReport::eChartLine); + reportCfg.setIncludingSchedules( false ); + reportCfg.addAccountGroup(MyMoneyAccount::Asset); + reportCfg.addAccountGroup(MyMoneyAccount::Liability); + reportCfg.setColumnsAreDays( true ); + reportCfg.setConvertCurrency( true ); + reportCfg.setIncludingForecast( true ); + reportCfg.setDateFilter(QDate::currentDate(),QDate::currentDate().addDays(+90)); + + reports::PivotTable table(reportCfg); + + reports::KReportChartView* chartWidget = new reports::KReportChartView(0, 0); + + table.drawChart(*chartWidget); + + chartWidget->params()->setLineMarker(false); + chartWidget->params()->setLegendPosition(KDChartParams::NoLegend); + chartWidget->params()->setLineWidth(2); + chartWidget->params()->setDataColor(0, KGlobalSettings::textColor()); + + // draw future values in a different line style + KDChartPropertySet propSetFutureValue("future value", KMM_KDCHART_PROPSET_NORMAL_DATA); + propSetFutureValue.setLineStyle(KDChartPropertySet::OwnID, Qt::DotLine); + const int idPropFutureValue = chartWidget->params()->registerProperties(propSetFutureValue); + + //KDChartPropertySet propSetLastValue("last value", idPropFutureValue); + //propSetLastValue.setExtraLinesAlign(KDChartPropertySet::OwnID, Qt::AlignLeft | Qt::AlignBottom); + //propSetLastValue.setExtraLinesWidth(KDChartPropertySet::OwnID, -4); + //propSetLastValue.setExtraLinesColor(KDChartPropertySet::OwnID, KMyMoneyGlobalSettings::listGridColor()); + // propSetLastValue.setShowMarker(KDChartPropertySet::OwnID, true); + // propSetLastValue.setMarkerStyle(KDChartPropertySet::OwnID, KDChartParams::LineMarkerDiamond); + + //const int idPropLastValue = chartWidget->params()->registerProperties(propSetLastValue); + for(int iCell = 0; iCell < 90; ++iCell) { + chartWidget->setProperty(0, iCell, idPropFutureValue); + } + //chartWidget->setProperty(0, 10, idPropLastValue); + + // Adjust the size + if(width() < chartWidget->width()) { + int nh; + nh = (width()*chartWidget->height() ) / chartWidget->width(); + chartWidget->resize(width()-80, nh); + } + + QPixmap pm(chartWidget->width(), chartWidget->height()); + pm.fill(KGlobalSettings::baseColor()); + QPainter p(&pm); + chartWidget->paintTo(p); + + QByteArray ba; + QBuffer buffer( ba ); + buffer.open( IO_WriteOnly ); + pm.save( &buffer, "PNG" ); // writes pixmap into ba in PNG format + + m_part->write("<table width=\"100%\" cellspacing=\"0\" cellpadding=\"2\" class=\"summarytable\" >"); + m_part->write("<tr>"); + m_part->write(QString("<td><center><IMG SRC=\"data:image/png;base64,%1\" ALT=\"Networth\"></center></td>").arg(KCodecs::base64Encode(ba))); + m_part->write("</tr>"); + m_part->write("</table></div></div>"); + + delete chartWidget; +#endif +} + +void KHomeView::showPayments(void) +{ + MyMoneyFile* file = MyMoneyFile::instance(); + QValueList<MyMoneySchedule> overdues; + QValueList<MyMoneySchedule> schedule; + int i = 0; + + //if forecast has not been executed yet, do it. + if(!m_forecast.isForecastDone()) + doForecast(); + + schedule = file->scheduleList("", MyMoneySchedule::TYPE_ANY, + MyMoneySchedule::OCCUR_ANY, + MyMoneySchedule::STYPE_ANY, + QDate::currentDate(), + QDate::currentDate().addMonths(1)); + overdues = file->scheduleList("", MyMoneySchedule::TYPE_ANY, + MyMoneySchedule::OCCUR_ANY, + MyMoneySchedule::STYPE_ANY, + QDate(), QDate(), true); + + if(schedule.empty() && overdues.empty()) + return; + + // HACK + // Remove the finished schedules + + QValueList<MyMoneySchedule>::Iterator d_it; + for (d_it=schedule.begin(); d_it!=schedule.end();) + { + // FIXME cleanup old code + // if ((*d_it).isFinished() || (*d_it).nextPayment((*d_it).lastPayment()) == QDate()) + if ((*d_it).isFinished()) + { + d_it = schedule.remove(d_it); + continue; + } + ++d_it; + } + + for (d_it=overdues.begin(); d_it!=overdues.end();) + { + // FIXME cleanup old code + // if ((*d_it).isFinished() || (*d_it).nextPayment((*d_it).lastPayment()) == QDate()) + if ((*d_it).isFinished()) + { + d_it = overdues.remove(d_it); + continue; + } + ++d_it; + } + + m_part->write("<div class=\"shadow\"><div class=\"displayblock\">"); + m_part->write(QString("<div class=\"summaryheader\">%1</div>\n").arg(i18n("Payments"))); + + if(overdues.count() > 0) { + m_part->write("<div class=\"gap\"> </div>\n"); + + qBubbleSort(overdues); + QValueList<MyMoneySchedule>::Iterator it; + QValueList<MyMoneySchedule>::Iterator it_f; + + m_part->write("<table width=\"100%\" cellspacing=\"0\" cellpadding=\"2\" class=\"summarytable\" >"); + m_part->write(QString("<tr class=\"itemtitle warningtitle\" ><td colspan=\"5\">%1</td></tr>\n").arg(showColoredAmount(i18n("Overdue payments"), true))); + m_part->write("<tr class=\"item warning\">"); + m_part->write("<td class=\"left\" width=\"10%\">"); + m_part->write(i18n("Date")); + m_part->write("</td>"); + m_part->write("<td class=\"left\" width=\"40%\">"); + m_part->write(i18n("Schedule")); + m_part->write("</td>"); + m_part->write("<td class=\"left\" width=\"20%\">"); + m_part->write(i18n("Account")); + m_part->write("</td>"); + m_part->write("<td class=\"right\" width=\"15%\">"); + m_part->write(i18n("Amount")); + m_part->write("</td>"); + m_part->write("<td class=\"right\" width=\"15%\">"); + m_part->write(i18n("Balance after")); + m_part->write("</td>"); + m_part->write("</tr>"); + + for(it = overdues.begin(); it != overdues.end(); ++it) { + // determine number of overdue payments + QDate nextDate = (*it).adjustedNextDueDate(); + int cnt = 0; + while(nextDate.isValid() && nextDate < QDate::currentDate()) { + ++cnt; + nextDate = (*it).nextPayment(nextDate); + // for single occurence nextDate will not change, so we + // better get out of here. + if((*it).occurence() == MyMoneySchedule::OCCUR_ONCE) + break; + } + + m_part->write(QString("<tr class=\"row-%1\">").arg(i++ & 0x01 ? "even" : "odd")); + showPaymentEntry(*it, cnt); + m_part->write("</tr>"); + // make sure to not repeat overdues later again + for(it_f = schedule.begin(); it_f != schedule.end();) { + if((*it).id() == (*it_f).id()) { + it_f = schedule.remove(it_f); + continue; + } + ++it_f; + } + } + m_part->write("</table>"); + } + + if(schedule.count() > 0) { + qBubbleSort(schedule); + + // Extract todays payments if any + QValueList<MyMoneySchedule> todays; + QValueList<MyMoneySchedule>::Iterator t_it; + for (t_it=schedule.begin(); t_it!=schedule.end();) { + if ((*t_it).nextDueDate() == QDate::currentDate()) { + todays.append(*t_it); + (*t_it).setNextDueDate((*t_it).nextPayment((*t_it).nextDueDate())); + + //if nextDueDate is still currentDate then remove it from scheduled payments + if ((*t_it).nextDueDate() == QDate::currentDate()) { + t_it = schedule.remove(t_it); + continue; + } + } + ++t_it; + } + + if (todays.count() > 0) { + m_part->write("<div class=\"gap\"> </div>\n"); + m_part->write("<table width=\"100%\" cellspacing=\"0\" cellpadding=\"2\" class=\"summarytable\" >"); + m_part->write(QString("<tr class=\"itemtitle\"><td class=\"left\" colspan=\"5\">%1</td></tr>\n").arg(i18n("Today's payments"))); + m_part->write("<tr class=\"item\">"); + m_part->write("<td class=\"left\" width=\"10%\">"); + m_part->write(i18n("Date")); + m_part->write("</td>"); + m_part->write("<td class=\"left\" width=\"40%\">"); + m_part->write(i18n("Schedule")); + m_part->write("</td>"); + m_part->write("<td class=\"left\" width=\"20%\">"); + m_part->write(i18n("Account")); + m_part->write("</td>"); + m_part->write("<td class=\"right\" width=\"15%\">"); + m_part->write(i18n("Amount")); + m_part->write("</td>"); + m_part->write("<td class=\"right\" width=\"15%\">"); + m_part->write(i18n("Balance after")); + m_part->write("</td>"); + m_part->write("</tr>"); + + for(t_it = todays.begin(); t_it != todays.end(); ++t_it) { + m_part->write(QString("<tr class=\"row-%1\">").arg(i++ & 0x01 ? "even" : "odd")); + showPaymentEntry(*t_it); + m_part->write("</tr>"); + } + m_part->write("</table>"); + } + + if (schedule.count() > 0) + { + m_part->write("<div class=\"gap\"> </div>\n"); + + QValueList<MyMoneySchedule>::Iterator it; + + m_part->write("<table width=\"100%\" cellspacing=\"0\" cellpadding=\"2\" class=\"summarytable\" >"); + m_part->write(QString("<tr class=\"itemtitle\"><td class=\"left\" colspan=\"5\">%1</td></tr>\n").arg(i18n("Future payments"))); + m_part->write("<tr class=\"item\">"); + m_part->write("<td class=\"left\" width=\"10%\">"); + m_part->write(i18n("Date")); + m_part->write("</td>"); + m_part->write("<td class=\"left\" width=\"40%\">"); + m_part->write(i18n("Schedule")); + m_part->write("</td>"); + m_part->write("<td class=\"left\" width=\"20%\">"); + m_part->write(i18n("Account")); + m_part->write("</td>"); + m_part->write("<td class=\"right\" width=\"15%\">"); + m_part->write(i18n("Amount")); + m_part->write("</td>"); + m_part->write("<td class=\"right\" width=\"15%\">"); + m_part->write(i18n("Balance after")); + m_part->write("</td>"); + m_part->write("</tr>"); + + // show all or the first 6 entries + int cnt; + cnt = (m_showAllSchedules) ? -1 : 6; + bool needMoreLess = m_showAllSchedules; + + QDate lastDate = QDate::currentDate().addMonths(1); + qBubbleSort(schedule); + do { + it = schedule.begin(); + if(it == schedule.end()) + break; + + // if the next due date is invalid (schedule is finished) + // we remove it from the list + QDate nextDate = (*it).nextDueDate(); + if(!nextDate.isValid()) { + schedule.remove(it); + continue; + } + + if (nextDate > lastDate) + break; + + if(cnt == 0) { + needMoreLess = true; + break; + } + if(cnt > 0) + --cnt; + + m_part->write(QString("<tr class=\"row-%1\">").arg(i++ & 0x01 ? "even" : "odd")); + showPaymentEntry(*it); + m_part->write("</tr>"); + + // for single occurence we have reported everything so we + // better get out of here. + if((*it).occurence() == MyMoneySchedule::OCCUR_ONCE) { + schedule.remove(it); + continue; + } + + (*it).setNextDueDate((*it).nextPayment((*it).nextDueDate())); + qBubbleSort(schedule); + } + while(1); + + if (needMoreLess) { + m_part->write(QString("<tr class=\"row-%1\">").arg(i++ & 0x01 ? "even" : "odd")); + m_part->write("<td>"); + if(m_showAllSchedules) { + m_part->write(link(VIEW_SCHEDULE, QString("?mode=%1").arg("reduced")) + i18n("Less...") + linkend()); + } else { + m_part->write(link(VIEW_SCHEDULE, QString("?mode=%1").arg("full")) + i18n("More...") + linkend()); + } + m_part->write("</td><td></td><td></td><td></td><td></td>"); + m_part->write("</tr>"); + } + m_part->write("</table>"); + } + } + m_part->write("</div></div>"); +} + +void KHomeView::showPaymentEntry(const MyMoneySchedule& sched, int cnt) +{ + QString tmp; + MyMoneyFile* file = MyMoneyFile::instance(); + + try { + MyMoneyAccount acc = sched.account(); + if(acc.id()) { + MyMoneyTransaction t = sched.transaction(); + // only show the entry, if it is still active + // FIXME clean old code + // if(!sched.isFinished() && sched.nextPayment(sched.lastPayment()) != QDate()) { + if(!sched.isFinished()) { + MyMoneySplit sp = t.splitByAccount(acc.id(), true); + + QString pathEnter, pathSkip; + KGlobal::iconLoader()->loadIcon("key_enter", KIcon::Small, KIcon::SizeSmall, KIcon::DefaultState, &pathEnter); + KGlobal::iconLoader()->loadIcon("player_fwd", KIcon::Small, KIcon::SizeSmall, KIcon::DefaultState, &pathSkip); + + //show payment date + tmp = QString("<td>") + + KGlobal::locale()->formatDate(sched.adjustedNextDueDate(), true) + + "</td><td>"; + if(pathEnter.length() > 0) + tmp += link(VIEW_SCHEDULE, QString("?id=%1&mode=enter").arg(sched.id()), i18n("Enter schedule")) + QString("<img src=\"file://%1\" border=\"0\"></a>").arg(pathEnter) + linkend(); + if(pathSkip.length() > 0) + tmp += " " + link(VIEW_SCHEDULE, QString("?id=%1&mode=skip").arg(sched.id()), i18n("Skip schedule")) + QString("<img src=\"file://%1\" border=\"0\"></a>").arg(pathSkip) + linkend(); + + tmp += QString(" "); + tmp += link(VIEW_SCHEDULE, QString("?id=%1&mode=edit").arg(sched.id()), i18n("Edit schedule")) + sched.name() + linkend(); + + //show quantity of payments overdue if any + if(cnt > 1) + tmp += i18n(" (%1 payments)").arg(cnt); + + //show account of the main split + tmp += "</td><td>"; + tmp += QString(file->account(acc.id()).name()); + + //show amount of the schedule + tmp += "</td><td align=\"right\">"; + + const MyMoneySecurity& currency = MyMoneyFile::instance()->currency(acc.currencyId()); + QString amount = (sp.value()*cnt).formatMoney(acc, currency); + amount.replace(" "," "); + tmp += showColoredAmount(amount, (sp.value()*cnt).isNegative()) ; + tmp += "</td>"; + //show balance after payments + tmp += "<td align=\"right\">"; + MyMoneyMoney payment = MyMoneyMoney((sp.value()*cnt)); + QDate paymentDate = QDate(sched.nextDueDate()); + MyMoneyMoney balanceAfter = forecastPaymentBalance(acc, payment, paymentDate); + QString balance = balanceAfter.formatMoney(acc, currency); + balance.replace(" "," "); + tmp += showColoredAmount(balance, balanceAfter.isNegative()); + tmp += "</td>"; + + // qDebug("paymentEntry = '%s'", tmp.latin1()); + m_part->write(tmp); + } + } + } catch(MyMoneyException* e) { + qDebug("Unable to display schedule entry: %s", e->what().data()); + delete e; + } +} + +void KHomeView::showAccounts(KHomeView::paymentTypeE type, const QString& header) +{ + MyMoneyFile* file = MyMoneyFile::instance(); + QValueList<MyMoneyAccount> accounts; + QValueList<MyMoneyAccount>::Iterator it; + QValueList<MyMoneyAccount>::Iterator prevIt; + QMap<QString, MyMoneyAccount> nameIdx; + + bool showClosedAccounts = kmymoney2->toggleAction("view_show_all_accounts")->isChecked(); + + // get list of all accounts + file->accountList(accounts); + for(it = accounts.begin(); it != accounts.end();) { + prevIt = it; + if(!(*it).isClosed() || showClosedAccounts) { + switch((*it).accountType()) { + case MyMoneyAccount::Expense: + case MyMoneyAccount::Income: + // never show a category account + // Note: This might be different in a future version when + // the homepage also shows category based information + it = accounts.remove(it); + break; + + // Asset and Liability accounts are only shown if they + // have the preferred flag set + case MyMoneyAccount::Asset: + case MyMoneyAccount::Liability: + case MyMoneyAccount::Investment: + // if preferred accounts are requested, then keep in list + if((*it).value("PreferredAccount") != "Yes" + || (type & Preferred) == 0) { + it = accounts.remove(it); + } + break; + + // Check payment accounts. If payment and preferred is selected, + // then always show them. If only payment is selected, then + // show only if preferred flag is not set. + case MyMoneyAccount::Checkings: + case MyMoneyAccount::Savings: + case MyMoneyAccount::Cash: + case MyMoneyAccount::CreditCard: + switch(type & (Payment | Preferred)) { + case Payment: + if((*it).value("PreferredAccount") == "Yes") + it = accounts.remove(it); + break; + + case Preferred: + if((*it).value("PreferredAccount") != "Yes") + it = accounts.remove(it); + break; + + case Payment | Preferred: + break; + + default: + it = accounts.remove(it); + break; + } + break; + + // filter all accounts that are not used on homepage views + default: + it = accounts.remove(it); + break; + } + + } else if((*it).isClosed() || (*it).isInvest()) { + // don't show if closed or a stock account + it = accounts.remove(it); + } + + // if we still point to the same account we keep it in the list and move on ;-) + if(prevIt == it) { + d->addNameIndex(nameIdx, *it); + ++it; + } + } + + if(accounts.count() > 0) { + QString tmp; + int i = 0; + tmp = "<div class=\"shadow\"><div class=\"displayblock\"><div class=\"summaryheader\">" + header + "</div>\n<div class=\"gap\"> </div>\n"; + m_part->write(tmp); + m_part->write("<table width=\"100%\" cellspacing=\"0\" cellpadding=\"2\" class=\"summarytable\" >"); + m_part->write("<tr class=\"item\"><td class=\"left\" width=\"35%\">"); + m_part->write(i18n("Account")); + m_part->write("</td><td width=\"25%\" class=\"right\">"); + m_part->write(i18n("Current Balance")); + m_part->write("</td>"); + //only show limit info if user chose to do so + if(KMyMoneyGlobalSettings::showLimitInfo()) { + m_part->write("<td width=\"40%\" class=\"right\">"); + m_part->write(i18n("To Minimum Balance / Maximum Credit")); + m_part->write("</td>"); + } + m_part->write("</tr>"); + + + QMap<QString, MyMoneyAccount>::const_iterator it_m; + for(it_m = nameIdx.begin(); it_m != nameIdx.end(); ++it_m) { + m_part->write(QString("<tr class=\"row-%1\">").arg(i++ & 0x01 ? "even" : "odd")); + showAccountEntry(*it_m); + m_part->write("</tr>"); + } + m_part->write("</table></div></div>"); + } +} + +void KHomeView::showAccountEntry(const MyMoneyAccount& acc) +{ + MyMoneyFile* file = MyMoneyFile::instance(); + MyMoneySecurity currency = file->currency(acc.currencyId()); + MyMoneyMoney value; + + bool showLimit = KMyMoneyGlobalSettings::showLimitInfo(); + + if(acc.accountType() == MyMoneyAccount::Investment) { + //investment accounts show the balances of all its subaccounts + value = investmentBalance(acc); + + //investment accounts have no minimum balance + showAccountEntry(acc, value, MyMoneyMoney(), showLimit); + } else { + //get balance for normal accounts + value = file->balance(acc.id(), QDate::currentDate()); + + //if credit card or checkings account, show maximum credit + if( acc.accountType() == MyMoneyAccount::CreditCard || + acc.accountType() == MyMoneyAccount::Checkings ) { + QString maximumCredit = acc.value("maxCreditAbsolute"); + MyMoneyMoney maxCredit = MyMoneyMoney(maximumCredit); + showAccountEntry(acc, value, value - maxCredit, showLimit); + } else { + //otherwise use minimum balance + QString minimumBalance = acc.value("minBalanceAbsolute"); + MyMoneyMoney minBalance = MyMoneyMoney(minimumBalance); + showAccountEntry(acc, value, value - minBalance, showLimit); + } + } +} + +void KHomeView::showAccountEntry(const MyMoneyAccount& acc, const MyMoneyMoney& value, const MyMoneyMoney& valueToMinBal, const bool showMinBal) +{ + MyMoneyFile* file = MyMoneyFile::instance(); + QString tmp; + MyMoneySecurity currency = file->currency(acc.currencyId()); + QString amount; + QString amountToMinBal; + + //format amounts + amount = value.formatMoney(acc, currency); + amount.replace(" "," "); + if(showMinBal) { + amountToMinBal = valueToMinBal.formatMoney(acc, currency); + amountToMinBal.replace(" "," "); + } + + tmp = QString("<td>") + + link(VIEW_LEDGER, QString("?id=%1").arg(acc.id())) + acc.name() + linkend() + "</td>"; + + //show account balance + tmp += QString("<td class=\"right\">%1</td>").arg(showColoredAmount(amount, value.isNegative())); + + //show minimum balance column if requested + if(showMinBal) { + //if it is an investment, show minimum balance empty + if(acc.accountType() == MyMoneyAccount::Investment) { + tmp += QString("<td class=\"right\"> </td>"); + } else { + //show minimum balance entry + tmp += QString("<td class=\"right\">%1</td>").arg(showColoredAmount(amountToMinBal, valueToMinBal.isNegative())); + } + } + // qDebug("accountEntry = '%s'", tmp.latin1()); + m_part->write(tmp); +} + +MyMoneyMoney KHomeView::investmentBalance(const MyMoneyAccount& acc) +{ + MyMoneyFile* file = MyMoneyFile::instance(); + MyMoneyMoney value; + + value = file->balance(acc.id()); + QValueList<QString>::const_iterator it_a; + for(it_a = acc.accountList().begin(); it_a != acc.accountList().end(); ++it_a) { + MyMoneyAccount stock = file->account(*it_a); + try { + MyMoneyMoney val; + MyMoneyMoney balance = file->balance(stock.id()); + MyMoneySecurity security = file->security(stock.currencyId()); + MyMoneyPrice price = file->price(stock.currencyId(), security.tradingCurrency()); + val = (balance * price.rate(security.tradingCurrency())).convert(MyMoneyMoney::precToDenom(KMyMoneyGlobalSettings::pricePrecision())); + // adjust value of security to the currency of the account + MyMoneySecurity accountCurrency = file->currency(acc.currencyId()); + val = val * file->price(security.tradingCurrency(), accountCurrency.id()).rate(accountCurrency.id()); + val = val.convert(acc.fraction()); + value += val; + } catch(MyMoneyException* e) { + qWarning("%s", (QString("cannot convert stock balance of %1 to base currency: %2").arg(stock.name(), e->what())).data()); + delete e; + } + } + return value; +} + +void KHomeView::showFavoriteReports(void) +{ + QValueList<MyMoneyReport> reports = MyMoneyFile::instance()->reportList(); + + if ( reports.count() > 0 ) + { + bool firstTime = 1; + int row = 0; + QValueList<MyMoneyReport>::const_iterator it_report = reports.begin(); + while( it_report != reports.end() ) + { + if ( (*it_report).isFavorite() ) { + if(firstTime) { + m_part->write(QString("<div class=\"shadow\"><div class=\"displayblock\"><div class=\"summaryheader\">%1</div>\n<div class=\"gap\"> </div>\n").arg(i18n("Favorite Reports"))); + m_part->write("<table width=\"100%\" cellspacing=\"0\" cellpadding=\"2\" class=\"summarytable\" >"); + m_part->write("<tr class=\"item\"><td class=\"left\" width=\"40%\">"); + m_part->write(i18n("Report")); + m_part->write("</td><td width=\"60%\" class=\"left\">"); + m_part->write(i18n("Comment")); + m_part->write("</td></tr>"); + firstTime = false; + } + + m_part->write(QString("<tr class=\"row-%1\"><td>%2%3%4</td><td align=\"left\">%5</td></tr>") + .arg(row++ & 0x01 ? "even" : "odd") + .arg(link(VIEW_REPORTS, QString("?id=%1").arg((*it_report).id()))) + .arg((*it_report).name()) + .arg(linkend()) + .arg((*it_report).comment()) + ); + } + + ++it_report; + } + if(!firstTime) + m_part->write("</table></div></div>"); + } +} + +void KHomeView::showForecast(void) +{ + QMap<QString, MyMoneyAccount> nameIdx; + MyMoneyFile* file = MyMoneyFile::instance(); + QValueList<MyMoneyAccount> accList; + + // if forecast has not been executed yet, do it. + if(!m_forecast.isForecastDone()) + doForecast(); + + accList = m_forecast.accountList(); + + // add it to a map to have it ordered by name + QValueList<MyMoneyAccount>::const_iterator accList_t = accList.begin(); + for ( ; accList_t != accList.end(); ++accList_t ) { + d->addNameIndex(nameIdx, *accList_t); + } + + if(nameIdx.count() > 0) { + int i = 0; + + int colspan = 1; + // get begin day + int beginDay = QDate::currentDate().daysTo(m_forecast.beginForecastDate()); + // if begin day is today skip to next cycle + if(beginDay == 0) + beginDay = m_forecast.accountsCycle(); + + // Now output header + m_part->write(QString("<div class=\"shadow\"><div class=\"displayblock\"><div class=\"summaryheader\">%1</div>\n<div class=\"gap\"> </div>\n").arg(i18n("%1 Day Forecast").arg(m_forecast.forecastDays()))); + m_part->write("<table width=\"100%\" cellspacing=\"0\" cellpadding=\"2\" class=\"summarytable\" >"); + m_part->write("<tr class=\"item\"><td class=\"left\" width=\"40%\">"); + m_part->write(i18n("Account")); + m_part->write("</td>"); + int colWidth = 55/ (m_forecast.forecastDays() / m_forecast.accountsCycle()); + for(i = 0; (i*m_forecast.accountsCycle() + beginDay) <= m_forecast.forecastDays(); ++i) { + m_part->write(QString("<td width=\"%1%\" class=\"right\">").arg(colWidth)); + + m_part->write(i18n("%1 days").arg(i*m_forecast.accountsCycle() + beginDay)); + m_part->write("</td>"); + colspan++; + } + m_part->write("</tr>"); + + // Now output entries + i = 0; + + QMap<QString, MyMoneyAccount>::ConstIterator it_account; + for(it_account = nameIdx.begin(); it_account != nameIdx.end(); ++it_account) { + //MyMoneyAccount acc = (*it_n); + + m_part->write(QString("<tr class=\"row-%1\">").arg(i++ & 0x01 ? "even" : "odd")); + m_part->write(QString("<td width=\"40%\">") + + link(VIEW_LEDGER, QString("?id=%1").arg((*it_account).id())) + (*it_account).name() + linkend() + "</td>"); + + int dropZero = -1; //account dropped below zero + int dropMinimum = -1; //account dropped below minimum balance + QString minimumBalance = (*it_account).value("minimumBalance"); + MyMoneyMoney minBalance = MyMoneyMoney(minimumBalance); + MyMoneySecurity currency; + MyMoneyMoney forecastBalance; + + //change account to deep currency if account is an investment + if((*it_account).isInvest()) { + MyMoneySecurity underSecurity = file->security((*it_account).currencyId()); + currency = file->security(underSecurity.tradingCurrency()); + } else { + currency = file->security((*it_account).currencyId()); + } + + for (int f = beginDay; f <= m_forecast.forecastDays(); f += m_forecast.accountsCycle()) { + forecastBalance = m_forecast.forecastBalance(*it_account, QDate::currentDate().addDays(f)); + QString amount; + amount = forecastBalance.formatMoney( *it_account, currency); + amount.replace(" "," "); + m_part->write(QString("<td width=\"%1%\" align=\"right\">").arg(colWidth)); + m_part->write(QString("%1</td>").arg(showColoredAmount(amount, forecastBalance.isNegative()))); + } + + m_part->write("</tr>"); + + //Check if the account is going to be below zero or below the minimal balance in the forecast period + + //Check if the account is going to be below minimal balance + dropMinimum = m_forecast.daysToMinimumBalance(*it_account); + + //Check if the account is going to be below zero in the future + dropZero = m_forecast.daysToZeroBalance(*it_account); + + + // spit out possible warnings + QString msg; + + // if a minimum balance has been specified, an appropriate warning will + // only be shown, if the drop below 0 is on a different day or not present + + if(dropMinimum != -1 + && !minBalance.isZero() + && (dropMinimum < dropZero + || dropZero == -1)) { + switch(dropMinimum) { + case -1: + break; + case 0: + msg = i18n("The balance of %1 is below the minimum balance %2 today.").arg((*it_account).name()).arg(minBalance.formatMoney(*it_account, currency)); + msg = showColoredAmount(msg, true); + break; + default: + msg = i18n("The balance of %1 will drop below the minimum balance %2 in %3 days.").arg((*it_account).name()).arg(minBalance.formatMoney(*it_account, currency)).arg(dropMinimum-1); + msg = showColoredAmount(msg, true); + break; + } + + if(!msg.isEmpty()) { + m_part->write(QString("<tr class=\"warning\" style=\"font-weight: normal;\" ><td colspan=%2 align=\"center\" >%1</td></tr>").arg(msg).arg(colspan)); + } + } + // a drop below zero is always shown + msg = QString(); + switch(dropZero) { + case -1: + break; + case 0: + if((*it_account).accountGroup() == MyMoneyAccount::Asset) { + msg = i18n("The balance of %1 is below %2 today.").arg((*it_account).name()).arg(MyMoneyMoney().formatMoney(*it_account, currency)); + msg = showColoredAmount(msg, true); + break; + } + if((*it_account).accountGroup() == MyMoneyAccount::Liability) { + msg = i18n("The balance of %1 is above %2 today.").arg((*it_account).name()).arg(MyMoneyMoney().formatMoney(*it_account, currency)); + break; + } + break; + default: + if((*it_account).accountGroup() == MyMoneyAccount::Asset) { + msg = i18n("The balance of %1 will drop below %2 in %3 days.").arg((*it_account).name()).arg(MyMoneyMoney().formatMoney(*it_account, currency)).arg(dropZero); + msg = showColoredAmount(msg, true); + break; + } + if((*it_account).accountGroup() == MyMoneyAccount::Liability) { + msg = i18n("The balance of %1 will raise above %2 in %3 days.").arg((*it_account).name()).arg(MyMoneyMoney().formatMoney(*it_account, currency)).arg(dropZero); + break; + } + } + if(!msg.isEmpty()) { + m_part->write(QString("<tr class=\"warning\"><td colspan=%2 align=\"center\" ><b>%1</b></td></tr>").arg(msg).arg(colspan)); + } + } + m_part->write("</table></div></div>"); + + } +} + +const QString KHomeView::link(const QString& view, const QString& query, const QString& _title) const +{ + QString titlePart; + QString title(_title); + if(!title.isEmpty()) + titlePart = QString(" title=\"%1\"").arg(title.replace(" ", " ")); + + return QString("<a href=\"/%1%2\"%3>").arg(view, query, titlePart); +} + +const QString KHomeView::linkend(void) const +{ + return "</a>"; +} + +void KHomeView::slotOpenURL(const KURL &url, const KParts::URLArgs& /* args */) +{ + QString protocol = url.protocol(); + QString view = url.fileName(false); + QString id = url.queryItem("id").data(); + QString mode = url.queryItem("mode").data(); + + if ( protocol == "http" ) + { + KApplication::kApplication()->invokeBrowser(url.prettyURL()); + } + else if ( protocol == "mailto" ) + { + KApplication::kApplication()->invokeMailer(url); + } + else + { + if(view == VIEW_LEDGER) { + emit ledgerSelected(id, QString()); + + } else if(view == VIEW_SCHEDULE) { + if(mode == "enter") { + emit scheduleSelected(id); + KMainWindow* mw = dynamic_cast<KMainWindow*>(qApp->mainWidget()); + Q_CHECK_PTR(mw); + QTimer::singleShot(0, mw->actionCollection()->action("schedule_enter"), SLOT(activate())); + + } else if(mode == "edit") { + emit scheduleSelected(id); + KMainWindow* mw = dynamic_cast<KMainWindow*>(qApp->mainWidget()); + Q_CHECK_PTR(mw); + QTimer::singleShot(0, mw->actionCollection()->action("schedule_edit"), SLOT(activate())); + + } else if(mode == "skip") { + emit scheduleSelected(id); + KMainWindow* mw = dynamic_cast<KMainWindow*>(qApp->mainWidget()); + Q_CHECK_PTR(mw); + QTimer::singleShot(0, mw->actionCollection()->action("schedule_skip"), SLOT(activate())); + + } else if(mode == "full") { + m_showAllSchedules = true; + loadView(); + + } else if(mode == "reduced") { + m_showAllSchedules = false; + loadView(); + } + + } else if(view == VIEW_REPORTS) { + emit reportSelected(id); + + } else if(view == VIEW_WELCOME) { + KMainWindow* mw = dynamic_cast<KMainWindow*>(qApp->mainWidget()); + Q_CHECK_PTR(mw); + if ( mode == "whatsnew" ) + { + QString fname = KMyMoneyUtils::findResource("appdata",QString("html/whats_new%1.html")); + if(!fname.isEmpty()) + m_part->openURL(fname); + } + else + m_part->openURL(m_filename); + + } else if(view == "action") { + KMainWindow* mw = dynamic_cast<KMainWindow*>(qApp->mainWidget()); + Q_CHECK_PTR(mw); + QTimer::singleShot(0, mw->actionCollection()->action( id ), SLOT(activate())); + + } else if(view == VIEW_HOME) { + QValueList<MyMoneyAccount> list; + MyMoneyFile::instance()->accountList(list); + if(list.count() == 0) { + KMessageBox::information(this, i18n("Before KMyMoney can give you detailed information about your financial status, you need to create at least one account. Until then, KMyMoney shows the welcome page instead.")); + } + loadView(); + + } else { + qDebug("Unknown view '%s' in KHomeView::slotOpenURL()", view.latin1()); + } + } +} + +void KHomeView::showAssetsLiabilities(void) +{ + QValueList<MyMoneyAccount> accounts; + QValueList<MyMoneyAccount>::Iterator it; + QMap<QString, MyMoneyAccount> nameAssetsIdx; + QMap<QString, MyMoneyAccount> nameLiabilitiesIdx; + MyMoneyMoney netAssets; + MyMoneyMoney netLiabilities; + QString fontStart, fontEnd; + + MyMoneyFile* file = MyMoneyFile::instance(); + int prec = MyMoneyMoney::denomToPrec(file->baseCurrency().smallestAccountFraction()); + int i = 0; + + + // get list of all accounts + file->accountList(accounts); + for(it = accounts.begin(); it != accounts.end();) { + if(!(*it).isClosed()) { + switch((*it).accountType()) { + // group all assets into one list but make sure that investment accounts always show up + case MyMoneyAccount::Investment: + d->addNameIndex(nameAssetsIdx, *it); + break; + + case MyMoneyAccount::Checkings: + case MyMoneyAccount::Savings: + case MyMoneyAccount::Cash: + case MyMoneyAccount::Asset: + case MyMoneyAccount::AssetLoan: + // list account if it's the last in the hierarchy or has transactions in it + if((*it).accountList().isEmpty() || (file->transactionCount((*it).id()) > 0)) { + d->addNameIndex(nameAssetsIdx, *it); + } + break; + + // group the liabilities into the other + case MyMoneyAccount::CreditCard: + case MyMoneyAccount::Liability: + case MyMoneyAccount::Loan: + // list account if it's the last in the hierarchy or has transactions in it + if((*it).accountList().isEmpty() || (file->transactionCount((*it).id()) > 0)) { + d->addNameIndex(nameLiabilitiesIdx, *it); + } + break; + + default: + break; + } + } + ++it; + } + + //only do it if we have assets or liabilities account + if(nameAssetsIdx.count() > 0 || nameLiabilitiesIdx.count() > 0) { + //print header + m_part->write("<div class=\"shadow\"><div class=\"displayblock\"><div class=\"summaryheader\">" + i18n("Assets and Liabilities Summary") + "</div>\n<div class=\"gap\"> </div>\n"); + m_part->write("<table width=\"100%\" cellspacing=\"0\" cellpadding=\"2\" class=\"summarytable\" >"); + //column titles + m_part->write("<tr class=\"item\"><td class=\"left\" width=\"30%\">"); + m_part->write(i18n("Asset Accounts")); + m_part->write("</td>"); + m_part->write("<td width=\"15%\" class=\"right\">"); + m_part->write(i18n("Current Balance")); + m_part->write("</td>"); + //intermediate row to separate both columns + m_part->write("<td width=\"10%\" class=\"setcolor\"></td>"); + m_part->write("<td class=\"left\" width=\"30%\">"); + m_part->write(i18n("Liability Accounts")); + m_part->write("</td>"); + m_part->write("<td width=\"15%\" class=\"right\">"); + m_part->write(i18n("Current Balance")); + m_part->write("</td></tr>"); + + //get asset and liability accounts + QMap<QString, MyMoneyAccount>::const_iterator asset_it = nameAssetsIdx.begin(); + QMap<QString,MyMoneyAccount>::const_iterator liabilities_it = nameLiabilitiesIdx.begin(); + for(; asset_it != nameAssetsIdx.end() || liabilities_it != nameLiabilitiesIdx.end();) { + m_part->write(QString("<tr class=\"row-%1\">").arg(i++ & 0x01 ? "even" : "odd")); + //write an asset account if we still have any + if(asset_it != nameAssetsIdx.end()) { + MyMoneyMoney value; + //investment accounts consolidate the balance of its subaccounts + if( (*asset_it).accountType() == MyMoneyAccount::Investment) { + value = investmentBalance(*asset_it); + } else { + value = MyMoneyFile::instance()->balance((*asset_it).id(), QDate::currentDate()); + } + //calculate balance for foreign currency accounts + if((*asset_it).currencyId() != file->baseCurrency().id()) { + ReportAccount repAcc = ReportAccount((*asset_it).id()); + MyMoneyMoney curPrice = repAcc.baseCurrencyPrice(QDate::currentDate()); + MyMoneyMoney baseValue = value * curPrice; + baseValue = baseValue.convert(10000); + netAssets += baseValue; + } else { + netAssets += value; + } + //show the account without minimum balance + showAccountEntry(*asset_it, value, MyMoneyMoney(), false); + ++asset_it; + } else { + //write a white space if we don't + m_part->write("<td></td><td></td>"); + } + + //leave the intermediate column empty + m_part->write("<td class=\"setcolor\"></td>"); + + //write a liability account + if(liabilities_it != nameLiabilitiesIdx.end()) { + MyMoneyMoney value; + value = MyMoneyFile::instance()->balance((*liabilities_it).id(), QDate::currentDate()); + //calculate balance if foreign currency + if((*liabilities_it).currencyId() != file->baseCurrency().id()) { + ReportAccount repAcc = ReportAccount((*liabilities_it).id()); + MyMoneyMoney curPrice = repAcc.baseCurrencyPrice(QDate::currentDate()); + MyMoneyMoney baseValue = value * curPrice; + baseValue = baseValue.convert(10000); + netLiabilities += baseValue; + } else { + netLiabilities += value; + } + //show the account without minimum balance + showAccountEntry(*liabilities_it, value, MyMoneyMoney(), false); + ++liabilities_it; + } else { + //leave the space empty if we run out of liabilities + m_part->write("<td></td><td></td>"); + } + m_part->write("</tr>"); + } + //calculate net worth + MyMoneyMoney netWorth = netAssets+netLiabilities; + + //format assets, liabilities and net worth + QString amountAssets = netAssets.formatMoney(file->baseCurrency().tradingSymbol(), prec); + QString amountLiabilities = netLiabilities.formatMoney(file->baseCurrency().tradingSymbol(), prec); + QString amountNetWorth = netWorth.formatMoney(file->baseCurrency().tradingSymbol(), prec); + amountAssets.replace(" "," "); + amountLiabilities.replace(" "," "); + amountNetWorth.replace(" "," "); + + m_part->write(QString("<tr class=\"row-%1\" style=\"font-weight:bold;\">").arg(i++ & 0x01 ? "even" : "odd")); + + //print total for assets + m_part->write(QString("<td class=\"left\">%1</td><td align=\"right\">%2</td>").arg(i18n("Total Assets")).arg(showColoredAmount(amountAssets, netAssets.isNegative()))); + + //leave the intermediate column empty + m_part->write("<td class=\"setcolor\"></td>"); + + //print total liabilities + m_part->write(QString("<td class=\"left\">%1</td><td align=\"right\">%2</td>").arg(i18n("Total Liabilities")).arg(showColoredAmount(amountLiabilities, netLiabilities.isNegative()))); + m_part->write("</tr>"); + + //print net worth + m_part->write(QString("<tr class=\"row-%1\" style=\"font-weight:bold;\">").arg(i++ & 0x01 ? "even" : "odd")); + + m_part->write("<td></td><td></td><td class=\"setcolor\"></td>"); + m_part->write(QString("<td class=\"left\">%1</td><td align=\"right\">%2</td>").arg(i18n("Net Worth")).arg(showColoredAmount(amountNetWorth, netWorth.isNegative() ))); + + m_part->write("</tr>"); + m_part->write("</table>"); + m_part->write("</div></div>"); + } +} + +void KHomeView::showBudget(void) +{ + MyMoneyFile* file = MyMoneyFile::instance(); + + if ( file->countBudgets() ) { + int prec = MyMoneyMoney::denomToPrec(file->baseCurrency().smallestAccountFraction()); + int i = 0; + + //config report just like "Monthly Budgeted vs Actual + MyMoneyReport reportCfg = MyMoneyReport( + MyMoneyReport::eBudgetActual, + MyMoneyReport::eMonths, + MyMoneyTransactionFilter::currentMonth, + MyMoneyReport::eDetailAll, + i18n("Monthly Budgeted vs. Actual"), + i18n("Generated Report")); + + reportCfg.setBudget("Any",true); + + reports::PivotTable table(reportCfg); + + PivotGrid grid = table.grid(); + + //div header + m_part->write("<div class=\"shadow\"><div class=\"displayblock\"><div class=\"summaryheader\">" + i18n("Budget") + "</div>\n<div class=\"gap\"> </div>\n"); + + //display budget summary + m_part->write("<table width=\"75%\" cellspacing=\"0\" cellpadding=\"2\" class=\"summarytable\" >"); + m_part->write("<tr class=\"itemtitle\">"); + m_part->write("<td class=\"left\" colspan=\"3\">"); + m_part->write(i18n("Current Month Summary")); + m_part->write("</td></tr>"); + m_part->write("<tr class=\"item\">"); + m_part->write("<td class=\"right\" width=\"33%\">"); + m_part->write(i18n("Budgeted")); + m_part->write("</td>"); + m_part->write("<td class=\"right\" width=\"33%\">"); + m_part->write(i18n("Actual")); + m_part->write("</td>"); + m_part->write("<td class=\"right\" width=\"33%\">"); + m_part->write(i18n("Difference")); + m_part->write("</td></tr>"); + + m_part->write(QString("<tr class=\"row-odd\">")); + + MyMoneyMoney totalBudgetValue = grid.m_total[eBudget].m_total; + MyMoneyMoney totalActualValue = grid.m_total[eActual].m_total; + MyMoneyMoney totalBudgetDiffValue = grid.m_total[eBudgetDiff].m_total; + + QString totalBudgetAmount = totalBudgetValue.formatMoney(file->baseCurrency().tradingSymbol(), prec); + QString totalActualAmount = totalActualValue.formatMoney(file->baseCurrency().tradingSymbol(), prec); + QString totalBudgetDiffAmount = totalBudgetDiffValue.formatMoney(file->baseCurrency().tradingSymbol(), prec); + + m_part->write(QString("<td align=\"right\">%1</td>").arg(showColoredAmount(totalBudgetAmount, totalBudgetValue.isNegative()))); + m_part->write(QString("<td align=\"right\">%1</td>").arg(showColoredAmount(totalActualAmount, totalActualValue.isNegative()))); + m_part->write(QString("<td align=\"right\">%1</td>").arg(showColoredAmount(totalBudgetDiffAmount, totalBudgetDiffValue.isNegative()))); + m_part->write("</tr>"); + m_part->write("</table>"); + + //budget overrun + m_part->write("<div class=\"gap\"> </div>\n"); + m_part->write("<table width=\"75%\" cellspacing=\"0\" cellpadding=\"2\" class=\"summarytable\" >"); + m_part->write("<tr class=\"itemtitle\">"); + m_part->write("<td class=\"left\" colspan=\"4\">"); + m_part->write(i18n("Budget Overruns")); + m_part->write("</td></tr>"); + m_part->write("<tr class=\"item\">"); + m_part->write("<td class=\"left\" width=\"30%\">"); + m_part->write(i18n("Account")); + m_part->write("</td>"); + m_part->write("<td class=\"right\" width=\"20%\">"); + m_part->write(i18n("Budgeted")); + m_part->write("</td>"); + m_part->write("<td class=\"right\" width=\"20%\">"); + m_part->write(i18n("Actual")); + m_part->write("</td>"); + m_part->write("<td class=\"right\" width=\"20%\">"); + m_part->write(i18n("Difference")); + m_part->write("</td></tr>"); + + + PivotGrid::iterator it_outergroup = grid.begin(); + while ( it_outergroup != grid.end() ) + { + i = 0; + PivotOuterGroup::iterator it_innergroup = (*it_outergroup).begin(); + while ( it_innergroup != (*it_outergroup).end() ) + { + PivotInnerGroup::iterator it_row = (*it_innergroup).begin(); + while ( it_row != (*it_innergroup).end() ) + { + //column number is 1 because the report includes only current month + if(it_row.data()[eBudgetDiff][1].isNegative()) { + //get report account to get the name later + ReportAccount rowname = it_row.key(); + + //write the outergroup if it is the first row of outergroup being shown + if(i == 0) { + m_part->write("<tr style=\"font-weight:bold;\">"); + m_part->write(QString("<td class=\"left\" colspan=\"4\">%1</td>").arg(KMyMoneyUtils::accountTypeToString( rowname.accountType()))); + m_part->write("</tr>"); + } + m_part->write(QString("<tr class=\"row-%1\">").arg(i++ & 0x01 ? "even" : "odd")); + + //get values from grid + MyMoneyMoney actualValue = it_row.data()[eActual][1]; + MyMoneyMoney budgetValue = it_row.data()[eBudget][1]; + MyMoneyMoney budgetDiffValue = it_row.data()[eBudgetDiff][1]; + + //format amounts + QString actualAmount = actualValue.formatMoney(file->baseCurrency().tradingSymbol(), prec); + QString budgetAmount = budgetValue.formatMoney(file->baseCurrency().tradingSymbol(), prec); + QString budgetDiffAmount = budgetDiffValue.formatMoney(file->baseCurrency().tradingSymbol(), prec); + + //account name + m_part->write(QString("<td>") + link(VIEW_LEDGER, QString("?id=%1").arg(rowname.id())) + rowname.name() + linkend() + "</td>"); + + //show amounts + m_part->write(QString("<td align=\"right\">%1</td>").arg(showColoredAmount(budgetAmount, budgetValue.isNegative()))); + m_part->write(QString("<td align=\"right\">%1</td>").arg(showColoredAmount(actualAmount, actualValue.isNegative()))); + m_part->write(QString("<td align=\"right\">%1</td>").arg(showColoredAmount(budgetDiffAmount, budgetDiffValue.isNegative()))); + m_part->write("</tr>"); + } + ++it_row; + } + ++it_innergroup; + } + ++it_outergroup; + } + + //if no negative differences are found, then inform that + if(i == 0) { + m_part->write(QString("<tr class=\"row-%1\" style=\"font-weight:bold;\">").arg(i++ & 0x01 ? "even" : "odd")); + m_part->write(QString("<td class=\"center\" colspan=\"4\">%1</td>").arg(i18n("No Budget Categories have been overrun"))); + m_part->write("</tr>"); + } + m_part->write("</table></div></div>"); + } +} + +QString KHomeView::showColoredAmount(const QString& amount, bool isNegative) +{ + if(isNegative) { + //if negative, get the settings for negative numbers + return QString("<font color=\"%1\">%2</font>").arg(KMyMoneyGlobalSettings::listNegativeValueColor().name(), amount); + } + + //if positive, return the same string + return amount; +} + +void KHomeView::doForecast(void) +{ + //clear m_accountList because forecast is about to changed + m_accountList.clear(); + + //reinitialize the object + m_forecast = MyMoneyForecast(); + + //If forecastDays lower than accountsCycle, adjust to the first cycle + if(m_forecast.accountsCycle() > m_forecast.forecastDays()) + m_forecast.setForecastDays(m_forecast.accountsCycle()); + + //Get all accounts of the right type to calculate forecast + m_forecast.doForecast(); +} + +MyMoneyMoney KHomeView::forecastPaymentBalance(const MyMoneyAccount& acc, const MyMoneyMoney& payment, QDate& paymentDate) +{ + //if paymentDate before or equal to currentDate set it to current date plus 1 + //so we get to accumulate forecast balance correctly + if(paymentDate <= QDate::currentDate()) + paymentDate = QDate::currentDate().addDays(1); + + //check if the account is already there + if(m_accountList.find(acc.id()) == m_accountList.end() + || m_accountList[acc.id()].find(paymentDate) == m_accountList[acc.id()].end()) + { + if(paymentDate == QDate::currentDate()) { + m_accountList[acc.id()][paymentDate] = m_forecast.forecastBalance(acc, paymentDate); + } else { + m_accountList[acc.id()][paymentDate] = m_forecast.forecastBalance(acc, paymentDate.addDays(-1)); + } + } + m_accountList[acc.id()][paymentDate] = m_accountList[acc.id()][paymentDate] + payment; + return m_accountList[acc.id()][paymentDate]; +} + +void KHomeView::showCashFlowSummary() +{ + MyMoneyTransactionFilter filter; + MyMoneyMoney incomeValue; + MyMoneyMoney expenseValue; + + MyMoneyFile* file = MyMoneyFile::instance(); + int prec = MyMoneyMoney::denomToPrec(file->baseCurrency().smallestAccountFraction()); + + //set start and end of month dates + QDate startOfMonth = QDate(QDate::currentDate().year(), QDate::currentDate().month(), 1); + QDate endOfMonth = QDate(QDate::currentDate().year(), QDate::currentDate().month(), QDate::currentDate().daysInMonth()); + + //Add total income and expenses for this month + //get transactions for current month + filter.setDateFilter(startOfMonth, endOfMonth); + filter.setReportAllSplits(false); + + QValueList<MyMoneyTransaction> transactions = file->transactionList(filter); + //if no transaction then skip and print total in zero + if(transactions.size() > 0) { + QValueList<MyMoneyTransaction>::const_iterator it_transaction; + + //get all transactions for this month + for(it_transaction = transactions.begin(); it_transaction != transactions.end(); ++it_transaction ) { + + //get the splits for each transaction + const QValueList<MyMoneySplit>& splits = (*it_transaction).splits(); + QValueList<MyMoneySplit>::const_iterator it_split; + for(it_split = splits.begin(); it_split != splits.end(); ++it_split) { + if(!(*it_split).shares().isZero()) { + ReportAccount repSplitAcc = ReportAccount((*it_split).accountId()); + + //only add if it is an income or expense + if(repSplitAcc.isIncomeExpense()) { + MyMoneyMoney value; + + //convert to base currency if necessary + if(repSplitAcc.currencyId() != file->baseCurrency().id()) { + MyMoneyMoney curPrice = repSplitAcc.baseCurrencyPrice((*it_transaction).postDate()); + value = ((*it_split).shares() * MyMoneyMoney(-1, 1)) * curPrice; + value = value.convert(10000); + } else { + value = ((*it_split).shares() * MyMoneyMoney(-1, 1)); + } + + //store depending on account type + if(repSplitAcc.accountType() == MyMoneyAccount::Income) { + incomeValue += value; + } else { + expenseValue += value; + } + } + } + } + } + } + + //format income and expenses + QString amountIncome = incomeValue.formatMoney(file->baseCurrency().tradingSymbol(), prec); + QString amountExpense = expenseValue.formatMoney(file->baseCurrency().tradingSymbol(), prec); + amountIncome.replace(" "," "); + amountExpense.replace(" "," "); + + //calculate schedules + + //Add all schedules for this month + MyMoneyMoney scheduledIncome; + MyMoneyMoney scheduledExpense; + MyMoneyMoney scheduledLiquidTransfer; + MyMoneyMoney scheduledOtherTransfer; + + //get overdues and schedules until the end of this month + QValueList<MyMoneySchedule> schedule = file->scheduleList("", MyMoneySchedule::TYPE_ANY, + MyMoneySchedule::OCCUR_ANY, + MyMoneySchedule::STYPE_ANY, + QDate(), + endOfMonth); + + //Remove the finished schedules + QValueList<MyMoneySchedule>::Iterator finished_it; + for (finished_it=schedule.begin(); finished_it!=schedule.end();) { + if ((*finished_it).isFinished()) { + finished_it = schedule.remove(finished_it); + continue; + } + ++finished_it; + } + + //add income and expenses + QValueList<MyMoneySchedule>::Iterator sched_it; + for (sched_it=schedule.begin(); sched_it!=schedule.end();) { + QDate nextDate = (*sched_it).nextDueDate(); + int cnt = 0; + + while(nextDate.isValid() && nextDate <= endOfMonth) { + ++cnt; + nextDate = (*sched_it).nextPayment(nextDate); + // for single occurence nextDate will not change, so we + // better get out of here. + if((*sched_it).occurence() == MyMoneySchedule::OCCUR_ONCE) + break; + } + + MyMoneyAccount acc = (*sched_it).account(); + if(acc.id()) { + MyMoneyTransaction transaction = (*sched_it).transaction(); + // only show the entry, if it is still active + + MyMoneySplit sp = transaction.splitByAccount(acc.id(), true); + + // take care of the autoCalc stuff + if((*sched_it).type() == MyMoneySchedule::TYPE_LOANPAYMENT) { + QDate nextDate = (*sched_it).nextPayment((*sched_it).lastPayment()); + + //make sure we have all 'starting balances' so that the autocalc works + QValueList<MyMoneySplit>::const_iterator it_s; + QMap<QString, MyMoneyMoney> balanceMap; + + for(it_s = transaction.splits().begin(); it_s != transaction.splits().end(); ++it_s ) { + MyMoneyAccount acc = file->account((*it_s).accountId()); + // collect all overdues on the first day + QDate schedDate = nextDate; + if(QDate::currentDate() >= nextDate) + schedDate = QDate::currentDate().addDays(1); + + balanceMap[acc.id()] += file->balance(acc.id()); + } + KMyMoneyUtils::calculateAutoLoan(*sched_it, transaction, balanceMap); + } + + //go through the splits and assign to liquid or other transfers + const QValueList<MyMoneySplit> splits = transaction.splits(); + QValueList<MyMoneySplit>::const_iterator split_it; + for (split_it = splits.begin(); split_it != splits.end(); ++split_it) { + if( (*split_it).accountId() != acc.id() ) { + ReportAccount repSplitAcc = ReportAccount((*split_it).accountId()); + + //get the shares and multiply by the quantity of occurences in the period + MyMoneyMoney value = (*split_it).shares() * cnt; + + //convert to foreign currency if needed + if(repSplitAcc.currencyId() != file->baseCurrency().id()) { + MyMoneyMoney curPrice = repSplitAcc.baseCurrencyPrice(QDate::currentDate()); + value = value * curPrice; + value = value.convert(10000); + } + + if(( repSplitAcc.isLiquidLiability() + || repSplitAcc.isLiquidAsset() ) + && acc.accountGroup() != repSplitAcc.accountGroup()) { + scheduledLiquidTransfer += value; + } else if(repSplitAcc.isAssetLiability() + && !repSplitAcc.isLiquidLiability() + && !repSplitAcc.isLiquidAsset() ) { + scheduledOtherTransfer += value; + } else if(repSplitAcc.isIncomeExpense()) { + //income and expenses are stored as negative values + if(repSplitAcc.accountType() == MyMoneyAccount::Income) + scheduledIncome -= value; + if(repSplitAcc.accountType() == MyMoneyAccount::Expense) + scheduledExpense -= value; + } + } + } + } + ++sched_it; + } + + //format the currency strings + QString amountScheduledIncome = scheduledIncome.formatMoney(file->baseCurrency().tradingSymbol(), prec); + QString amountScheduledExpense = scheduledExpense.formatMoney(file->baseCurrency().tradingSymbol(), prec); + QString amountScheduledLiquidTransfer = scheduledLiquidTransfer.formatMoney(file->baseCurrency().tradingSymbol(), prec); + QString amountScheduledOtherTransfer = scheduledOtherTransfer.formatMoney(file->baseCurrency().tradingSymbol(), prec); + + amountScheduledIncome.replace(" "," "); + amountScheduledExpense.replace(" "," "); + amountScheduledLiquidTransfer.replace(" "," "); + amountScheduledOtherTransfer.replace(" "," "); + + //get liquid assets and liabilities + QValueList<MyMoneyAccount> accounts; + QValueList<MyMoneyAccount>::const_iterator account_it; + MyMoneyMoney liquidAssets; + MyMoneyMoney liquidLiabilities; + + // get list of all accounts + file->accountList(accounts); + for(account_it = accounts.begin(); account_it != accounts.end();) { + if(!(*account_it).isClosed()) { + switch((*account_it).accountType()) { + //group all assets into one list + case MyMoneyAccount::Checkings: + case MyMoneyAccount::Savings: + case MyMoneyAccount::Cash: + { + MyMoneyMoney value = MyMoneyFile::instance()->balance((*account_it).id(), QDate::currentDate()); + //calculate balance for foreign currency accounts + if((*account_it).currencyId() != file->baseCurrency().id()) { + ReportAccount repAcc = ReportAccount((*account_it).id()); + MyMoneyMoney curPrice = repAcc.baseCurrencyPrice(QDate::currentDate()); + MyMoneyMoney baseValue = value * curPrice; + liquidAssets += baseValue; + liquidAssets = liquidAssets.convert(10000); + } else { + liquidAssets += value; + } + break; + } + //group the liabilities into the other + case MyMoneyAccount::CreditCard: + { + MyMoneyMoney value; + value = MyMoneyFile::instance()->balance((*account_it).id(), QDate::currentDate()); + //calculate balance if foreign currency + if((*account_it).currencyId() != file->baseCurrency().id()) { + ReportAccount repAcc = ReportAccount((*account_it).id()); + MyMoneyMoney curPrice = repAcc.baseCurrencyPrice(QDate::currentDate()); + MyMoneyMoney baseValue = value * curPrice; + liquidLiabilities += baseValue; + liquidLiabilities = liquidLiabilities.convert(10000); + } else { + liquidLiabilities += value; + } + break; + } + default: + break; + } + } + ++account_it; + } + //calculate net worth + MyMoneyMoney liquidWorth = liquidAssets+liquidLiabilities; + + //format assets, liabilities and net worth + QString amountLiquidAssets = liquidAssets.formatMoney(file->baseCurrency().tradingSymbol(), prec); + QString amountLiquidLiabilities = liquidLiabilities.formatMoney(file->baseCurrency().tradingSymbol(), prec); + QString amountLiquidWorth = liquidWorth.formatMoney(file->baseCurrency().tradingSymbol(), prec); + amountLiquidAssets.replace(" "," "); + amountLiquidLiabilities.replace(" "," "); + amountLiquidWorth.replace(" "," "); + + //show the summary + m_part->write("<div class=\"shadow\"><div class=\"displayblock\"><div class=\"summaryheader\">" + i18n("Cash Flow Summary") + "</div>\n<div class=\"gap\"> </div>\n"); + + //print header + m_part->write("<table width=\"100%\" cellspacing=\"0\" cellpadding=\"2\" class=\"summarytable\" >"); + //income and expense title + m_part->write("<tr class=\"itemtitle\">"); + m_part->write("<td class=\"left\" colspan=\"4\">"); + m_part->write(i18n("Income and Expenses of Current Month")); + m_part->write("</td></tr>"); + //column titles + m_part->write("<tr class=\"item\">"); + m_part->write("<td width=\"25%\" class=\"center\">"); + m_part->write(i18n("Income")); + m_part->write("</td>"); + m_part->write("<td width=\"25%\" class=\"center\">"); + m_part->write(i18n("Scheduled Income")); + m_part->write("</td>"); + m_part->write("<td width=\"25%\" class=\"center\">"); + m_part->write(i18n("Expenses")); + m_part->write("</td>"); + m_part->write("<td width=\"25%\" class=\"center\">"); + m_part->write(i18n("Scheduled Expenses")); + m_part->write("</td>"); + m_part->write("</tr>"); + + //add row with banding + m_part->write(QString("<tr class=\"row-even\" style=\"font-weight:bold;\">")); + + //print current income + m_part->write(QString("<td align=\"right\">%2</td>").arg(showColoredAmount(amountIncome, incomeValue.isNegative()))); + + //print the scheduled income + m_part->write(QString("<td align=\"right\">%2</td>").arg(showColoredAmount(amountScheduledIncome, scheduledIncome.isNegative()))); + + //print current expenses + m_part->write(QString("<td align=\"right\">%2</td>").arg(showColoredAmount(amountExpense, expenseValue.isNegative()))); + + //print the scheduled expenses + m_part->write(QString("<td align=\"right\">%2</td>").arg(showColoredAmount(amountScheduledExpense, scheduledExpense.isNegative()))); + m_part->write("</tr>"); + + m_part->write("</table>"); + + //print header of assets and liabilities + m_part->write("<div class=\"gap\"> </div>\n"); + m_part->write("<table width=\"100%\" cellspacing=\"0\" cellpadding=\"2\" class=\"summarytable\" >"); + //assets and liabilities title + m_part->write("<tr class=\"itemtitle\">"); + m_part->write("<td class=\"left\" colspan=\"4\">"); + m_part->write(i18n("Liquid Assets and Liabilities")); + m_part->write("</td></tr>"); + //column titles + m_part->write("<tr class=\"item\">"); + m_part->write("<td width=\"25%\" class=\"center\">"); + m_part->write(i18n("Liquid Assets")); + m_part->write("</td>"); + m_part->write("<td width=\"25%\" class=\"center\">"); + m_part->write(i18n("Transfers to Liquid Liabilities")); + m_part->write("</td>"); + m_part->write("<td width=\"25%\" class=\"center\">"); + m_part->write(i18n("Liquid Liabilities")); + m_part->write("</td>"); + m_part->write("<td width=\"25%\" class=\"center\">"); + m_part->write(i18n("Other Transfers")); + m_part->write("</td>"); + m_part->write("</tr>"); + + //add row with banding + m_part->write(QString("<tr class=\"row-even\" style=\"font-weight:bold;\">")); + + //print current liquid assets + m_part->write(QString("<td align=\"right\">%2</td>").arg(showColoredAmount(amountLiquidAssets, liquidAssets.isNegative()))); + + //print the scheduled transfers + m_part->write(QString("<td align=\"right\">%2</td>").arg(showColoredAmount(amountScheduledLiquidTransfer, scheduledLiquidTransfer.isNegative()))); + + //print current liabilities + m_part->write(QString("<td align=\"right\">%2</td>").arg(showColoredAmount(amountLiquidLiabilities, liquidLiabilities.isNegative()))); + + //print the scheduled transfers + m_part->write(QString("<td align=\"right\">%2</td>").arg(showColoredAmount(amountScheduledOtherTransfer, scheduledOtherTransfer.isNegative()))); + + + m_part->write("</tr>"); + + m_part->write("</table>"); + + //final conclusion + MyMoneyMoney profitValue = incomeValue + expenseValue + scheduledIncome + scheduledExpense; + MyMoneyMoney expectedAsset = liquidAssets + scheduledIncome + scheduledExpense + scheduledLiquidTransfer + scheduledOtherTransfer; + MyMoneyMoney expectedLiabilities = liquidLiabilities + scheduledLiquidTransfer; + + QString amountExpectedAsset = expectedAsset.formatMoney(file->baseCurrency().tradingSymbol(), prec); + QString amountExpectedLiabilities = expectedLiabilities.formatMoney(file->baseCurrency().tradingSymbol(), prec); + QString amountProfit = profitValue.formatMoney(file->baseCurrency().tradingSymbol(), prec); + amountProfit.replace(" "," "); + amountExpectedAsset.replace(" "," "); + amountExpectedLiabilities.replace(" "," "); + + + + //print header of cash flow status + m_part->write("<div class=\"gap\"> </div>\n"); + m_part->write("<table width=\"100%\" cellspacing=\"0\" cellpadding=\"2\" class=\"summarytable\" >"); + //income and expense title + m_part->write("<tr class=\"itemtitle\">"); + m_part->write("<td class=\"left\" colspan=\"4\">"); + m_part->write(i18n("Cash Flow Status")); + m_part->write("</td></tr>"); + //column titles + m_part->write("<tr class=\"item\">"); + m_part->write("<td> </td>"); + m_part->write("<td width=\"25%\" class=\"center\">"); + m_part->write(i18n("Expected Liquid Assets")); + m_part->write("</td>"); + m_part->write("<td width=\"25%\" class=\"center\">"); + m_part->write(i18n("Expected Liquid Liabilities")); + m_part->write("</td>"); + m_part->write("<td width=\"25%\" class=\"center\">"); + m_part->write(i18n("Expected Profit/Loss")); + m_part->write("</td>"); + m_part->write("</tr>"); + + //add row with banding + m_part->write(QString("<tr class=\"row-even\" style=\"font-weight:bold;\">")); + m_part->write("<td> </td>"); + + //print expected assets + m_part->write(QString("<td align=\"right\">%2</td>").arg(showColoredAmount(amountExpectedAsset, expectedAsset.isNegative()))); + + //print expected liabilities + m_part->write(QString("<td align=\"right\">%2</td>").arg(showColoredAmount(amountExpectedLiabilities, expectedLiabilities.isNegative()))); + + //print expected profit + m_part->write(QString("<td align=\"right\">%2</td>").arg(showColoredAmount(amountProfit, profitValue.isNegative()))); + + m_part->write("</tr>"); + + m_part->write("</table>"); + + m_part->write("</div></div>"); + + +} + +// Make sure, that these definitions are only used within this file +// this does not seem to be necessary, but when building RPMs the +// build option 'final' is used and all CPP files are concatenated. +// So it could well be, that in another CPP file these definitions +// are also used. +#undef VIEW_LEDGER +#undef VIEW_SCHEDULE +#undef VIEW_WELCOME +#undef VIEW_HOME +#undef VIEW_REPORTS + +#include "khomeview.moc" |