You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
kmymoney/kmymoney2/views/khomeview.cpp

1948 lines
74 KiB

/***************************************************************************
khomeview.cpp - description
-------------------
begin : Tue Jan 22 2002
copyright : (C) 2000-2002 by Michael Edwardes
email : mte@users.sourceforge.net
Javier Campos Morales <javi_c@users.sourceforge.net>
Felix Rodriguez <frodriguez@users.sourceforge.net>
John C <thetacoturtle@users.sourceforge.net>
Thomas Baumgart <ipwizard@users.sourceforge.net>
Kevin Tambascio <ktambascio@users.sourceforge.net>
***************************************************************************/
/***************************************************************************
* *
* 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 <tqlayout.h>
#include <tqdatetime.h>
#include <tqapplication.h>
#include <dom/dom_element.h>
#include <dom/dom_doc.h>
#include <dom/dom_text.h>
#include <tqfile.h>
#include <tqtextstream.h>
#include <tqtimer.h>
#include <tqbuffer.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(TQMap<TQString, MyMoneyAccount> &idx, const MyMoneyAccount& account);
};
void KHomeView::Private::addNameIndex(TQMap<TQString, MyMoneyAccount> &idx, const MyMoneyAccount& account)
{
TQString 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(TQWidget *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", TQString("html/home%1.html"));
// m_part->openURL(m_filename);
connect(m_part->browserExtension(), TQT_SIGNAL(openURLRequest(const KURL&, const KParts::URLArgs&)),
this, TQT_SLOT(slotOpenURL(const KURL&, const KParts::URLArgs&)));
connect(MyMoneyFile::instance(), TQT_SIGNAL(dataChanged()), this, TQT_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;
}
TQWidget::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;
TQValueList<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!");
TQString 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);
TQString filename = KGlobal::dirs()->findResource("appdata", "html/kmymoney2.css");
TQString header = TQString("<!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";
TQString footer = "</body></html>\n";
m_part->begin();
m_part->write(header);
m_part->write(TQString("<div id=\"summarytitle\">%1</div>").arg(i18n("Your Financial Summary")));
TQStringList settings = KMyMoneyGlobalSettings::itemList();
TQStringList::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\">&nbsp;</div>\n");
}
}
m_part->write("<div id=\"returnlink\">");
m_part->write(link(VIEW_WELCOME, TQString()) + 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(TQString("<div class=\"shadow\"><div class=\"displayblock\"><div class=\"summaryheader\">%1</div>\n<div class=\"gap\">&nbsp;</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(TQDate::currentDate(),TQDate::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, TQt::AlignLeft | TQt::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);
}
TQPixmap pm(chartWidget->width(), chartWidget->height());
pm.fill(KGlobalSettings::baseColor());
TQPainter p(&pm);
chartWidget->paintTo(p);
TQByteArray ba;
TQBuffer 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(TQString("<td><center><IMG SRC=\"data:image/png;base64,%1\" ALT=\"Networth\"></center></td>").arg(TQString(KCodecs::base64Encode(ba))));
m_part->write("</tr>");
m_part->write("</table></div></div>");
delete chartWidget;
#endif
}
void KHomeView::showPayments(void)
{
MyMoneyFile* file = MyMoneyFile::instance();
TQValueList<MyMoneySchedule> overdues;
TQValueList<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,
TQDate::currentDate(),
TQDate::currentDate().addMonths(1));
overdues = file->scheduleList("", MyMoneySchedule::TYPE_ANY,
MyMoneySchedule::OCCUR_ANY,
MyMoneySchedule::STYPE_ANY,
TQDate(), TQDate(), true);
if(schedule.empty() && overdues.empty())
return;
// HACK
// Remove the finished schedules
TQValueList<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()) == TQDate())
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()) == TQDate())
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(TQString("<div class=\"summaryheader\">%1</div>\n").arg(i18n("Payments")));
if(overdues.count() > 0) {
m_part->write("<div class=\"gap\">&nbsp;</div>\n");
qBubbleSort(overdues);
TQValueList<MyMoneySchedule>::Iterator it;
TQValueList<MyMoneySchedule>::Iterator it_f;
m_part->write("<table width=\"100%\" cellspacing=\"0\" cellpadding=\"2\" class=\"summarytable\" >");
m_part->write(TQString("<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
TQDate nextDate = (*it).adjustedNextDueDate();
int cnt = 0;
while(nextDate.isValid() && nextDate < TQDate::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(TQString("<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
TQValueList<MyMoneySchedule> todays;
TQValueList<MyMoneySchedule>::Iterator t_it;
for (t_it=schedule.begin(); t_it!=schedule.end();) {
if ((*t_it).nextDueDate() == TQDate::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() == TQDate::currentDate()) {
t_it = schedule.remove(t_it);
continue;
}
}
++t_it;
}
if (todays.count() > 0) {
m_part->write("<div class=\"gap\">&nbsp;</div>\n");
m_part->write("<table width=\"100%\" cellspacing=\"0\" cellpadding=\"2\" class=\"summarytable\" >");
m_part->write(TQString("<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(TQString("<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\">&nbsp;</div>\n");
TQValueList<MyMoneySchedule>::Iterator it;
m_part->write("<table width=\"100%\" cellspacing=\"0\" cellpadding=\"2\" class=\"summarytable\" >");
m_part->write(TQString("<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;
TQDate lastDate = TQDate::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
TQDate 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(TQString("<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;
}
//if nextPayment returns an invalid date, setNextDueDate will just skip it, resulting in a loop
//we check the resulting date and erase the schedule if invalid
if(!((*it).nextPayment((*it).nextDueDate())).isValid()) {
schedule.remove(it);
continue;
}
(*it).setNextDueDate((*it).nextPayment((*it).nextDueDate()));
qBubbleSort(schedule);
}
while(1);
if (needMoreLess) {
m_part->write(TQString("<tr class=\"row-%1\">").arg(i++ & 0x01 ? "even" : "odd"));
m_part->write("<td>");
if(m_showAllSchedules) {
m_part->write(link(VIEW_SCHEDULE, TQString("?mode=%1").arg("reduced")) + i18n("Less...") + linkend());
} else {
m_part->write(link(VIEW_SCHEDULE, TQString("?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)
{
TQString 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()) != TQDate()) {
if(!sched.isFinished()) {
MyMoneySplit sp = t.splitByAccount(acc.id(), true);
TQString 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 = TQString("<td>") +
KGlobal::locale()->formatDate(sched.adjustedNextDueDate(), true) +
"</td><td>";
if(pathEnter.length() > 0)
tmp += link(VIEW_SCHEDULE, TQString("?id=%1&mode=enter").arg(sched.id()), i18n("Enter schedule")) + TQString("<img src=\"file://%1\" border=\"0\"></a>").arg(pathEnter) + linkend();
if(pathSkip.length() > 0)
tmp += "&nbsp;" + link(VIEW_SCHEDULE, TQString("?id=%1&mode=skip").arg(sched.id()), i18n("Skip schedule")) + TQString("<img src=\"file://%1\" border=\"0\"></a>").arg(pathSkip) + linkend();
tmp += TQString("&nbsp;");
tmp += link(VIEW_SCHEDULE, TQString("?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 += TQString(file->account(acc.id()).name());
//show amount of the schedule
tmp += "</td><td align=\"right\">";
const MyMoneySecurity& currency = MyMoneyFile::instance()->currency(acc.currencyId());
TQString amount = (sp.value()*cnt).formatMoney(acc, currency);
amount.replace(" ","&nbsp;");
tmp += showColoredAmount(amount, (sp.value()*cnt).isNegative()) ;
tmp += "</td>";
//show balance after payments
tmp += "<td align=\"right\">";
MyMoneyMoney payment = MyMoneyMoney((sp.value()*cnt));
TQDate paymentDate = TQDate(sched.nextDueDate());
MyMoneyMoney balanceAfter = forecastPaymentBalance(acc, payment, paymentDate);
TQString balance = balanceAfter.formatMoney(acc, currency);
balance.replace(" ","&nbsp;");
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 TQString& header)
{
MyMoneyFile* file = MyMoneyFile::instance();
TQValueList<MyMoneyAccount> accounts;
TQValueList<MyMoneyAccount>::Iterator it;
TQValueList<MyMoneyAccount>::Iterator prevIt;
TQMap<TQString, 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) {
TQString tmp;
int i = 0;
tmp = "<div class=\"shadow\"><div class=\"displayblock\"><div class=\"summaryheader\">" + header + "</div>\n<div class=\"gap\">&nbsp;</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>");
TQMap<TQString, MyMoneyAccount>::const_iterator it_m;
for(it_m = nameIdx.begin(); it_m != nameIdx.end(); ++it_m) {
m_part->write(TQString("<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(), TQDate::currentDate());
//if credit card or checkings account, show maximum credit
if( acc.accountType() == MyMoneyAccount::CreditCard ||
acc.accountType() == MyMoneyAccount::Checkings ) {
TQString maximumCredit = acc.value("maxCreditAbsolute");
MyMoneyMoney maxCredit = MyMoneyMoney(maximumCredit);
showAccountEntry(acc, value, value - maxCredit, showLimit);
} else {
//otherwise use minimum balance
TQString 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();
TQString tmp;
MyMoneySecurity currency = file->currency(acc.currencyId());
TQString amount;
TQString amountToMinBal;
//format amounts
amount = value.formatMoney(acc, currency);
amount.replace(" ","&nbsp;");
if(showMinBal) {
amountToMinBal = valueToMinBal.formatMoney(acc, currency);
amountToMinBal.replace(" ","&nbsp;");
}
tmp = TQString("<td>") +
link(VIEW_LEDGER, TQString("?id=%1").arg(acc.id())) + acc.name() + linkend() + "</td>";
//show account balance
tmp += TQString("<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 += TQString("<td class=\"right\">&nbsp;</td>");
} else {
//show minimum balance entry
tmp += TQString("<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());
TQValueList<TQString>::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", (TQString("cannot convert stock balance of %1 to base currency: %2").arg(stock.name(), e->what())).data());
delete e;
}
}
return value;
}
void KHomeView::showFavoriteReports(void)
{
TQValueList<MyMoneyReport> reports = MyMoneyFile::instance()->reportList();
if ( reports.count() > 0 )
{
bool firstTime = 1;
int row = 0;
TQValueList<MyMoneyReport>::const_iterator it_report = reports.begin();
while( it_report != reports.end() )
{
if ( (*it_report).isFavorite() ) {
if(firstTime) {
m_part->write(TQString("<div class=\"shadow\"><div class=\"displayblock\"><div class=\"summaryheader\">%1</div>\n<div class=\"gap\">&nbsp;</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(TQString("<tr class=\"row-%1\"><td>%2%3%4</td><td align=\"left\">%5</td></tr>")
.arg(row++ & 0x01 ? "even" : "odd")
.arg(link(VIEW_REPORTS, TQString("?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)
{
TQMap<TQString, MyMoneyAccount> nameIdx;
MyMoneyFile* file = MyMoneyFile::instance();
TQValueList<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
TQValueList<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 = TQDate::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(TQString("<div class=\"shadow\"><div class=\"displayblock\"><div class=\"summaryheader\">%1</div>\n<div class=\"gap\">&nbsp;</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(TQString("<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;
TQMap<TQString, MyMoneyAccount>::ConstIterator it_account;
for(it_account = nameIdx.begin(); it_account != nameIdx.end(); ++it_account) {
//MyMoneyAccount acc = (*it_n);
m_part->write(TQString("<tr class=\"row-%1\">").arg(i++ & 0x01 ? "even" : "odd"));
m_part->write(TQString("<td width=\"40%\">") +
link(VIEW_LEDGER, TQString("?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
TQString 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, TQDate::currentDate().addDays(f));
TQString amount;
amount = forecastBalance.formatMoney( *it_account, currency);
amount.replace(" ","&nbsp;");
m_part->write(TQString("<td width=\"%1%\" align=\"right\">").arg(colWidth));
m_part->write(TQString("%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
TQString 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(TQString("<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 = TQString();
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(TQString("<tr class=\"warning\"><td colspan=%2 align=\"center\" ><b>%1</b></td></tr>").arg(msg).arg(colspan));
}
}
m_part->write("</table></div></div>");
}
}
const TQString KHomeView::link(const TQString& view, const TQString& query, const TQString& _title) const
{
TQString titlePart;
TQString title(_title);
if(!title.isEmpty())
titlePart = TQString(" title=\"%1\"").arg(title.replace(" ", "&nbsp;"));
return TQString("<a href=\"/%1%2\"%3>").arg(view, query, titlePart);
}
const TQString KHomeView::linkend(void) const
{
return "</a>";
}
void KHomeView::slotOpenURL(const KURL &url, const KParts::URLArgs& /* args */)
{
TQString protocol = url.protocol();
TQString view = url.fileName(false);
TQString id = url.queryItem("id");
TQString mode = url.queryItem("mode");
if ( protocol == "http" )
{
KApplication::kApplication()->invokeBrowser(url.prettyURL());
}
else if ( protocol == "mailto" )
{
KApplication::kApplication()->invokeMailer(url);
}
else
{
if(view == VIEW_LEDGER) {
emit ledgerSelected(id, TQString());
} else if(view == VIEW_SCHEDULE) {
if(mode == "enter") {
emit scheduleSelected(id);
KMainWindow* mw = dynamic_cast<KMainWindow*>(tqApp->mainWidget());
TQ_CHECK_PTR(mw);
TQTimer::singleShot(0, mw->actionCollection()->action("schedule_enter"), TQT_SLOT(activate()));
} else if(mode == "edit") {
emit scheduleSelected(id);
KMainWindow* mw = dynamic_cast<KMainWindow*>(tqApp->mainWidget());
TQ_CHECK_PTR(mw);
TQTimer::singleShot(0, mw->actionCollection()->action("schedule_edit"), TQT_SLOT(activate()));
} else if(mode == "skip") {
emit scheduleSelected(id);
KMainWindow* mw = dynamic_cast<KMainWindow*>(tqApp->mainWidget());
TQ_CHECK_PTR(mw);
TQTimer::singleShot(0, mw->actionCollection()->action("schedule_skip"), TQT_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*>(tqApp->mainWidget());
TQ_CHECK_PTR(mw);
if ( mode == "whatsnew" )
{
TQString fname = KMyMoneyUtils::findResource("appdata",TQString("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*>(tqApp->mainWidget());
TQ_CHECK_PTR(mw);
TQTimer::singleShot(0, mw->actionCollection()->action( id ), TQT_SLOT(activate()));
} else if(view == VIEW_HOME) {
TQValueList<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)
{
TQValueList<MyMoneyAccount> accounts;
TQValueList<MyMoneyAccount>::Iterator it;
TQMap<TQString, MyMoneyAccount> nameAssetsIdx;
TQMap<TQString, MyMoneyAccount> nameLiabilitiesIdx;
MyMoneyMoney netAssets;
MyMoneyMoney netLiabilities;
TQString 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\">&nbsp;</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
TQMap<TQString, MyMoneyAccount>::const_iterator asset_it = nameAssetsIdx.begin();
TQMap<TQString,MyMoneyAccount>::const_iterator liabilities_it = nameLiabilitiesIdx.begin();
for(; asset_it != nameAssetsIdx.end() || liabilities_it != nameLiabilitiesIdx.end();) {
m_part->write(TQString("<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(), TQDate::currentDate());
}
//calculate balance for foreign currency accounts
if((*asset_it).currencyId() != file->baseCurrency().id()) {
ReportAccount repAcc = ReportAccount((*asset_it).id());
MyMoneyMoney curPrice = repAcc.baseCurrencyPrice(TQDate::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(), TQDate::currentDate());
//calculate balance if foreign currency
if((*liabilities_it).currencyId() != file->baseCurrency().id()) {
ReportAccount repAcc = ReportAccount((*liabilities_it).id());
MyMoneyMoney curPrice = repAcc.baseCurrencyPrice(TQDate::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
TQString amountAssets = netAssets.formatMoney(file->baseCurrency().tradingSymbol(), prec);
TQString amountLiabilities = netLiabilities.formatMoney(file->baseCurrency().tradingSymbol(), prec);
TQString amountNetWorth = netWorth.formatMoney(file->baseCurrency().tradingSymbol(), prec);
amountAssets.replace(" ","&nbsp;");
amountLiabilities.replace(" ","&nbsp;");
amountNetWorth.replace(" ","&nbsp;");
m_part->write(TQString("<tr class=\"row-%1\" style=\"font-weight:bold;\">").arg(i++ & 0x01 ? "even" : "odd"));
//print total for assets
m_part->write(TQString("<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(TQString("<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(TQString("<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(TQString("<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\">&nbsp;</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(TQString("<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;
TQString totalBudgetAmount = totalBudgetValue.formatMoney(file->baseCurrency().tradingSymbol(), prec);
TQString totalActualAmount = totalActualValue.formatMoney(file->baseCurrency().tradingSymbol(), prec);
TQString totalBudgetDiffAmount = totalBudgetDiffValue.formatMoney(file->baseCurrency().tradingSymbol(), prec);
m_part->write(TQString("<td align=\"right\">%1</td>").arg(showColoredAmount(totalBudgetAmount, totalBudgetValue.isNegative())));
m_part->write(TQString("<td align=\"right\">%1</td>").arg(showColoredAmount(totalActualAmount, totalActualValue.isNegative())));
m_part->write(TQString("<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\">&nbsp;</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(TQString("<td class=\"left\" colspan=\"4\">%1</td>").arg(KMyMoneyUtils::accountTypeToString( rowname.accountType())));
m_part->write("</tr>");
}
m_part->write(TQString("<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
TQString actualAmount = actualValue.formatMoney(file->baseCurrency().tradingSymbol(), prec);
TQString budgetAmount = budgetValue.formatMoney(file->baseCurrency().tradingSymbol(), prec);
TQString budgetDiffAmount = budgetDiffValue.formatMoney(file->baseCurrency().tradingSymbol(), prec);
//account name
m_part->write(TQString("<td>") + link(VIEW_LEDGER, TQString("?id=%1").arg(rowname.id())) + rowname.name() + linkend() + "</td>");
//show amounts
m_part->write(TQString("<td align=\"right\">%1</td>").arg(showColoredAmount(budgetAmount, budgetValue.isNegative())));
m_part->write(TQString("<td align=\"right\">%1</td>").arg(showColoredAmount(actualAmount, actualValue.isNegative())));
m_part->write(TQString("<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(TQString("<tr class=\"row-%1\" style=\"font-weight:bold;\">").arg(i++ & 0x01 ? "even" : "odd"));
m_part->write(TQString("<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>");
}
}
TQString KHomeView::showColoredAmount(const TQString& amount, bool isNegative)
{
if(isNegative) {
//if negative, get the settings for negative numbers
return TQString("<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, TQDate& 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 <= TQDate::currentDate())
paymentDate = TQDate::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 == TQDate::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
TQDate startOfMonth = TQDate(TQDate::currentDate().year(), TQDate::currentDate().month(), 1);
TQDate endOfMonth = TQDate(TQDate::currentDate().year(), TQDate::currentDate().month(), TQDate::currentDate().daysInMonth());
//Add total income and expenses for this month
//get transactions for current month
filter.setDateFilter(startOfMonth, endOfMonth);
filter.setReportAllSplits(false);
TQValueList<MyMoneyTransaction> transactions = file->transactionList(filter);
//if no transaction then skip and print total in zero
if(transactions.size() > 0) {
TQValueList<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 TQValueList<MyMoneySplit>& splits = (*it_transaction).splits();
TQValueList<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
TQString amountIncome = incomeValue.formatMoney(file->baseCurrency().tradingSymbol(), prec);
TQString amountExpense = expenseValue.formatMoney(file->baseCurrency().tradingSymbol(), prec);
amountIncome.replace(" ","&nbsp;");
amountExpense.replace(" ","&nbsp;");
//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
TQValueList<MyMoneySchedule> schedule = file->scheduleList("", MyMoneySchedule::TYPE_ANY,
MyMoneySchedule::OCCUR_ANY,
MyMoneySchedule::STYPE_ANY,
TQDate(),
endOfMonth);
//Remove the finished schedules
TQValueList<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
TQValueList<MyMoneySchedule>::Iterator sched_it;
for (sched_it=schedule.begin(); sched_it!=schedule.end();) {
TQDate 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) {
TQDate nextDate = (*sched_it).nextPayment((*sched_it).lastPayment());
//make sure we have all 'starting balances' so that the autocalc works
TQValueList<MyMoneySplit>::const_iterator it_s;
TQMap<TQString, 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
TQDate schedDate = nextDate;
if(TQDate::currentDate() >= nextDate)
schedDate = TQDate::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 TQValueList<MyMoneySplit> splits = transaction.splits();
TQValueList<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(TQDate::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
TQString amountScheduledIncome = scheduledIncome.formatMoney(file->baseCurrency().tradingSymbol(), prec);
TQString amountScheduledExpense = scheduledExpense.formatMoney(file->baseCurrency().tradingSymbol(), prec);
TQString amountScheduledLiquidTransfer = scheduledLiquidTransfer.formatMoney(file->baseCurrency().tradingSymbol(), prec);
TQString amountScheduledOtherTransfer = scheduledOtherTransfer.formatMoney(file->baseCurrency().tradingSymbol(), prec);
amountScheduledIncome.replace(" ","&nbsp;");
amountScheduledExpense.replace(" ","&nbsp;");
amountScheduledLiquidTransfer.replace(" ","&nbsp;");
amountScheduledOtherTransfer.replace(" ","&nbsp;");
//get liquid assets and liabilities
TQValueList<MyMoneyAccount> accounts;
TQValueList<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(), TQDate::currentDate());
//calculate balance for foreign currency accounts
if((*account_it).currencyId() != file->baseCurrency().id()) {
ReportAccount repAcc = ReportAccount((*account_it).id());
MyMoneyMoney curPrice = repAcc.baseCurrencyPrice(TQDate::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(), TQDate::currentDate());
//calculate balance if foreign currency
if((*account_it).currencyId() != file->baseCurrency().id()) {
ReportAccount repAcc = ReportAccount((*account_it).id());
MyMoneyMoney curPrice = repAcc.baseCurrencyPrice(TQDate::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
TQString amountLiquidAssets = liquidAssets.formatMoney(file->baseCurrency().tradingSymbol(), prec);
TQString amountLiquidLiabilities = liquidLiabilities.formatMoney(file->baseCurrency().tradingSymbol(), prec);
TQString amountLiquidWorth = liquidWorth.formatMoney(file->baseCurrency().tradingSymbol(), prec);
amountLiquidAssets.replace(" ","&nbsp;");
amountLiquidLiabilities.replace(" ","&nbsp;");
amountLiquidWorth.replace(" ","&nbsp;");
//show the summary
m_part->write("<div class=\"shadow\"><div class=\"displayblock\"><div class=\"summaryheader\">" + i18n("Cash Flow Summary") + "</div>\n<div class=\"gap\">&nbsp;</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(TQString("<tr class=\"row-even\" style=\"font-weight:bold;\">"));
//print current income
m_part->write(TQString("<td align=\"right\">%2</td>").arg(showColoredAmount(amountIncome, incomeValue.isNegative())));
//print the scheduled income
m_part->write(TQString("<td align=\"right\">%2</td>").arg(showColoredAmount(amountScheduledIncome, scheduledIncome.isNegative())));
//print current expenses
m_part->write(TQString("<td align=\"right\">%2</td>").arg(showColoredAmount(amountExpense, expenseValue.isNegative())));
//print the scheduled expenses
m_part->write(TQString("<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\">&nbsp;</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(TQString("<tr class=\"row-even\" style=\"font-weight:bold;\">"));
//print current liquid assets
m_part->write(TQString("<td align=\"right\">%2</td>").arg(showColoredAmount(amountLiquidAssets, liquidAssets.isNegative())));
//print the scheduled transfers
m_part->write(TQString("<td align=\"right\">%2</td>").arg(showColoredAmount(amountScheduledLiquidTransfer, scheduledLiquidTransfer.isNegative())));
//print current liabilities
m_part->write(TQString("<td align=\"right\">%2</td>").arg(showColoredAmount(amountLiquidLiabilities, liquidLiabilities.isNegative())));
//print the scheduled transfers
m_part->write(TQString("<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;
TQString amountExpectedAsset = expectedAsset.formatMoney(file->baseCurrency().tradingSymbol(), prec);
TQString amountExpectedLiabilities = expectedLiabilities.formatMoney(file->baseCurrency().tradingSymbol(), prec);
TQString amountProfit = profitValue.formatMoney(file->baseCurrency().tradingSymbol(), prec);
amountProfit.replace(" ","&nbsp;");
amountExpectedAsset.replace(" ","&nbsp;");
amountExpectedLiabilities.replace(" ","&nbsp;");
//print header of cash flow status
m_part->write("<div class=\"gap\">&nbsp;</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>&nbsp;</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(TQString("<tr class=\"row-even\" style=\"font-weight:bold;\">"));
m_part->write("<td>&nbsp;</td>");
//print expected assets
m_part->write(TQString("<td align=\"right\">%2</td>").arg(showColoredAmount(amountExpectedAsset, expectedAsset.isNegative())));
//print expected liabilities
m_part->write(TQString("<td align=\"right\">%2</td>").arg(showColoredAmount(amountExpectedLiabilities, expectedLiabilities.isNegative())));
//print expected profit
m_part->write(TQString("<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"