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/reports/objectinfotable.cpp

372 lines
14 KiB

/***************************************************************************
objectinfotable.cpp
-------------------
begin : Sat 28 jun 2008
copyright : (C) 2004-2005 by Ace Jones
2008 by Alvaro Soliverez
email : acejones@users.sourceforge.net
asoliverez@gmail.com
***************************************************************************/
/***************************************************************************
* *
* 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. *
* *
***************************************************************************/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
// ----------------------------------------------------------------------------
// TQt Includes
#include <tqvaluelist.h>
#include <tqfile.h>
#include <tqtextstream.h>
// ----------------------------------------------------------------------------
// TDE Includes
// This is just needed for i18n(). Once I figure out how to handle i18n
// without using this macro directly, I'll be freed of KDE dependency.
#include <tdelocale.h>
#include <kdebug.h>
// ----------------------------------------------------------------------------
// Project Includes
#include "../mymoney/mymoneyfile.h"
#include "../mymoney/mymoneyreport.h"
#include "../mymoney/mymoneyexception.h"
#include "../kmymoneyutils.h"
#include "reportaccount.h"
#include "reportdebug.h"
#include "objectinfotable.h"
namespace reports {
// ****************************************************************************
//
// ObjectInfoTable implementation
//
// ****************************************************************************
/**
* TODO
*
* - Collapse 2- & 3- groups when they are identical
* - Way more test cases (especially splits & transfers)
* - Option to collapse splits
* - Option to exclude transfers
*
*/
ObjectInfoTable::ObjectInfoTable(const MyMoneyReport& _report): ListTable(_report)
{
// seperated into its own method to allow debugging (setting breakpoints
// directly in ctors somehow does not work for me (ipwizard))
// TODO: remove the init() method and move the code back to the ctor
init();
}
void ObjectInfoTable::init ( void )
{
switch ( m_config.rowType() )
{
case MyMoneyReport::eSchedule:
constructScheduleTable();
m_columns = "nextduedate,name";
break;
case MyMoneyReport::eAccountInfo:
constructAccountTable();
m_columns = "institution,type,name";
break;
case MyMoneyReport::eAccountLoanInfo:
constructAccountLoanTable();
m_columns = "institution,type,name";
break;
default:
break;
}
// Sort the data to match the report definition
m_subtotal="value";
switch ( m_config.rowType() )
{
case MyMoneyReport::eSchedule:
m_group = "type";
m_subtotal="value";
break;
case MyMoneyReport::eAccountInfo:
case MyMoneyReport::eAccountLoanInfo:
m_group = "topcategory,institution";
m_subtotal="currentbalance";
break;
default:
throw new MYMONEYEXCEPTION ( "ObjectInfoTable::ObjectInfoTable(): unhandled row type" );
}
TQString sort = m_group + "," + m_columns + ",id,rank";
switch ( m_config.rowType() ) {
case MyMoneyReport::eSchedule:
if ( m_config.detailLevel() == MyMoneyReport::eDetailAll ) {
m_columns="name,payee,paymenttype,occurence,nextduedate,category";
} else {
m_columns="name,payee,paymenttype,occurence,nextduedate";
}
break;
case MyMoneyReport::eAccountInfo:
m_columns="type,name,number,description,openingdate,currencyname,balancewarning,maxbalancelimit,creditwarning,maxcreditlimit,tax,favorite";
break;
case MyMoneyReport::eAccountLoanInfo:
m_columns="type,name,number,description,openingdate,currencyname,payee,loanamount,interestrate,nextinterestchange,periodicpayment,finalpayment,favorite";
break;
default:
m_columns = "";
}
TableRow::setSortCriteria ( sort );
qHeapSort ( m_rows );
}
void ObjectInfoTable::constructScheduleTable ( void )
{
MyMoneyFile* file = MyMoneyFile::instance();
TQValueList<MyMoneySchedule> schedules;
schedules = file->scheduleList ( "", MyMoneySchedule::TYPE_ANY, MyMoneySchedule::OCCUR_ANY, MyMoneySchedule::STYPE_ANY, m_config.fromDate(), m_config.toDate() );
TQValueList<MyMoneySchedule>::const_iterator it_schedule = schedules.begin();
while ( it_schedule != schedules.end() )
{
MyMoneySchedule schedule = *it_schedule;
ReportAccount account = schedule.account();
if ( m_config.includes ( account ) ) {
//get fraction for account
int fraction = account.fraction();
//use base currency fraction if not initialized
if ( fraction == -1 )
fraction = MyMoneyFile::instance()->baseCurrency().smallestAccountFraction();
TableRow scheduleRow;
//convert to base currency if needed
MyMoneyMoney xr = MyMoneyMoney(1,1);
if (m_config.isConvertCurrency() && account.isForeignCurrency()) {
xr = account.baseCurrencyPrice(TQDate::currentDate()).reduce();
}
// help for sort and render functions
scheduleRow["rank"] = "0";
//schedule data
scheduleRow["id"] = schedule.id();
scheduleRow["name"] = schedule.name();
scheduleRow["nextduedate"] = schedule.nextDueDate().toString ( TQt::ISODate );
scheduleRow["type"] = KMyMoneyUtils::scheduleTypeToString ( schedule.type() );
scheduleRow["occurence"] = i18n(schedule.occurenceToString().utf8());
scheduleRow["paymenttype"] = KMyMoneyUtils::paymentMethodToString ( schedule.paymentType() );
//scheduleRow["category"] = account.name();
//to get the payee we must look into the splits of the transaction
MyMoneyTransaction transaction = schedule.transaction();
MyMoneySplit split = transaction.splitByAccount ( account.id(), true );
scheduleRow["value"] = (split.value() * xr).toString();
MyMoneyPayee payee = file->payee ( split.payeeId() );
scheduleRow["payee"] = payee.name();
m_rows += scheduleRow;
//the text matches the main split
bool transaction_text = m_config.match(&split);
if ( m_config.detailLevel() == MyMoneyReport::eDetailAll )
{
//get the information for all splits
TQValueList<MyMoneySplit> splits = transaction.splits();
TQValueList<MyMoneySplit>::const_iterator split_it = splits.begin();
for ( ;split_it != splits.end(); split_it++ )
{
TableRow splitRow;
ReportAccount splitAcc = ( *split_it ).accountId();
splitRow["rank"] = "1";
splitRow["id"] = schedule.id();
splitRow["name"] = schedule.name();
splitRow["type"] = KMyMoneyUtils::scheduleTypeToString ( schedule.type() );
splitRow["nextduedate"] = schedule.nextDueDate().toString ( TQt::ISODate );
if ( ( *split_it ).value() == MyMoneyMoney::autoCalc ) {
splitRow["split"] = MyMoneyMoney::autoCalc.toString();
} else if ( ! splitAcc.isIncomeExpense() ) {
splitRow["split"] = ( *split_it ).value().toString();
} else {
splitRow["split"] = ( - ( *split_it ).value() ).toString();
}
//if it is an assett account, mark it as a transfer
if ( ! splitAcc.isIncomeExpense() ) {
splitRow["category"] = ( ( * split_it ).value().isNegative() )
? i18n ( "Transfer from %1" ).arg ( splitAcc.fullName() )
: i18n ( "Transfer to %1" ).arg ( splitAcc.fullName() );
} else {
splitRow ["category"] = splitAcc.fullName();
}
//add the split only if it matches the text or it matches the main split
if(m_config.match( &(*split_it) )
|| transaction_text )
m_rows += splitRow;
}
}
}
++it_schedule;
}
}
void ObjectInfoTable::constructAccountTable ( void )
{
MyMoneyFile* file = MyMoneyFile::instance();
//make sure we have all subaccounts of investment accounts
includeInvestmentSubAccounts();
TQValueList<MyMoneyAccount> accounts;
file->accountList(accounts);
TQValueList<MyMoneyAccount>::const_iterator it_account = accounts.begin();
while ( it_account != accounts.end() )
{
TableRow accountRow;
ReportAccount account = *it_account;
if(m_config.includes(account)
&& account.accountType() != MyMoneyAccount::Stock
&& !account.isClosed())
{
MyMoneyMoney value;
accountRow["rank"] = "0";
accountRow["topcategory"] = KMyMoneyUtils::accountTypeToString(account.accountGroup());
accountRow["institution"] = (file->institution(account.institutionId())).name();
accountRow["type"] = KMyMoneyUtils::accountTypeToString(account.accountType());
accountRow["name"] = account.name();
accountRow["number"] = account.number();
accountRow["description"] = account.description();
accountRow["openingdate"] = account.openingDate().toString( TQt::ISODate );
//accountRow["currency"] = (file->currency(account.currencyId())).tradingSymbol();
accountRow["currencyname"] = (file->currency(account.currencyId())).name();
accountRow["balancewarning"] = account.value("minBalanceEarly");
accountRow["maxbalancelimit"] = account.value("minBalanceAbsolute");
accountRow["creditwarning"] = account.value("maxCreditEarly");
accountRow["maxcreditlimit"] = account.value("maxCreditAbsolute");
accountRow["tax"] = account.value("Tax");
accountRow["favorite"] = account.value("PreferredAccount");
//investment accounts show the balances of all its subaccounts
if(account.accountType() == MyMoneyAccount::Investment) {
value = investmentBalance(account);
} else {
value = file->balance(account.id());
}
//convert to base currency if needed
if (m_config.isConvertCurrency() && account.isForeignCurrency()) {
MyMoneyMoney xr = account.baseCurrencyPrice(TQDate::currentDate()).reduce();
value = value * xr;
}
accountRow["currentbalance"] = value.toString();
m_rows += accountRow;
}
++it_account;
}
}
void ObjectInfoTable::constructAccountLoanTable ( void )
{
MyMoneyFile* file = MyMoneyFile::instance();
TQValueList<MyMoneyAccount> accounts;
file->accountList(accounts);
TQValueList<MyMoneyAccount>::const_iterator it_account = accounts.begin();
while ( it_account != accounts.end() )
{
TableRow accountRow;
ReportAccount account = *it_account;
MyMoneyAccountLoan loan = *it_account;
if(m_config.includes(account) &&
( account.accountType() == MyMoneyAccount::Loan
|| account.accountType() == MyMoneyAccount::AssetLoan )
&& !account.isClosed())
{
//convert to base currency if needed
MyMoneyMoney xr = MyMoneyMoney(1,1);
if (m_config.isConvertCurrency() && account.isForeignCurrency()) {
xr = account.baseCurrencyPrice(TQDate::currentDate()).reduce();
}
accountRow["rank"] = "0";
accountRow["topcategory"] = KMyMoneyUtils::accountTypeToString(account.accountGroup());
accountRow["institution"] = (file->institution(account.institutionId())).name();
accountRow["type"] = KMyMoneyUtils::accountTypeToString(account.accountType());
accountRow["name"] = account.name();
accountRow["number"] = account.number();
accountRow["description"] = account.description();
accountRow["openingdate"] = account.openingDate().toString( TQt::ISODate );
//accountRow["currency"] = (file->currency(account.currencyId())).tradingSymbol();
accountRow["currencyname"] = (file->currency(account.currencyId())).name();
accountRow["payee"] = file->payee(loan.payee()).name();
accountRow["loanamount"] = (loan.loanAmount() * xr).toString();
accountRow["interestrate"] = (loan.interestRate(TQDate::currentDate())/MyMoneyMoney(100,1)*xr).toString();
accountRow["nextinterestchange"] = loan.nextInterestChange().toString( TQt::ISODate );
accountRow["periodicpayment"] = (loan.periodicPayment() * xr).toString();
accountRow["finalpayment"] = (loan.finalPayment() * xr).toString();
accountRow["favorite"] = account.value("PreferredAccount");
MyMoneyMoney value = file->balance(account.id());
value = value * xr;
accountRow["currentbalance"] = value.toString();
m_rows += accountRow;
}
++it_account;
}
}
MyMoneyMoney ObjectInfoTable::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());
// 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) {
tqWarning(TQString("cannot convert stock balance of %1 to base currency: %2").arg(stock.name(), e->what()));
delete e;
}
}
return value;
}
}