/***************************************************************************
listtable . 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
// ----------------------------------------------------------------------------
// QT Includes
# include <tqvaluelist.h>
# include <tqfile.h>
# include <tqtextstream.h>
// ----------------------------------------------------------------------------
// KDE 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 "../kmymoneyglobalsettings.h"
# include "reportdebug.h"
# include "listtable.h"
namespace reports {
TQStringList ListTable : : TableRow : : m_sortCriteria ;
// ****************************************************************************
//
// Group Iterator
//
// ****************************************************************************
class GroupIterator
{
public :
GroupIterator ( const TQString & _group , const TQString & _subtotal , unsigned _depth ) : m_depth ( _depth ) , m_groupField ( _group ) , m_subtotalField ( _subtotal ) { }
GroupIterator ( void ) { }
void update ( const ListTable : : TableRow & _row )
{
m_previousGroup = m_currentGroup ;
m_currentGroup = _row [ m_groupField ] ;
if ( isSubtotal ( ) )
{
m_previousSubtotal = m_currentSubtotal ;
m_currentSubtotal = MyMoneyMoney ( ) ;
}
m_currentSubtotal + = _row [ m_subtotalField ] ;
}
bool isNewHeader ( void ) const { return ( m_currentGroup ! = m_previousGroup ) ; }
bool isSubtotal ( void ) const { return ( m_currentGroup ! = m_previousGroup ) & & ( ! m_previousGroup . isEmpty ( ) ) ; }
const MyMoneyMoney & subtotal ( void ) const { return m_previousSubtotal ; }
const MyMoneyMoney & currenttotal ( void ) const { return m_currentSubtotal ; }
unsigned depth ( void ) const { return m_depth ; }
const TQString & name ( void ) const { return m_currentGroup ; }
const TQString & oldName ( void ) const { return m_previousGroup ; }
const TQString & groupField ( void ) const { return m_groupField ; }
const TQString & subtotalField ( void ) const { return m_subtotalField ; }
// ***DV*** HACK make the currentGroup test different but look the same
void force ( void ) { m_currentGroup + = " " ; }
private :
MyMoneyMoney m_currentSubtotal ;
MyMoneyMoney m_previousSubtotal ;
unsigned m_depth ;
TQString m_currentGroup ;
TQString m_previousGroup ;
TQString m_groupField ;
TQString m_subtotalField ;
} ;
// ****************************************************************************
//
// ListTable implementation
//
// ****************************************************************************
bool ListTable : : TableRow : : operator < ( const TableRow & _compare ) const
{
bool result = false ;
TQStringList : : const_iterator it_criterion = m_sortCriteria . begin ( ) ;
while ( it_criterion ! = m_sortCriteria . end ( ) )
{
if ( this - > operator [ ] ( * it_criterion ) < _compare [ * it_criterion ] )
{
result = true ;
break ;
}
else if ( this - > operator [ ] ( * it_criterion ) > _compare [ * it_criterion ] )
break ;
+ + it_criterion ;
}
return result ;
}
// needed for KDE < 3.2 implementation of qHeapSort
bool ListTable : : TableRow : : operator < = ( const TableRow & _compare ) const
{
return ( ! ( _compare < * this ) ) ;
}
bool ListTable : : TableRow : : operator = = ( const TableRow & _compare ) const
{
return ( ! ( * this < _compare ) & & ! ( _compare < * this ) ) ;
}
bool ListTable : : TableRow : : operator > ( const TableRow & _compare ) const
{
return ( _compare < * this ) ;
}
/**
* TODO
*
* - Collapse 2 - & 3 - groups when they are identical
* - Way more test cases ( especially splits & transfers )
* - Option to collapse splits
* - Option to exclude transfers
*
*/
ListTable : : ListTable ( const MyMoneyReport & _report ) : m_config ( _report )
{
}
void ListTable : : render ( TQString & result , TQString & csv ) const
{
MyMoneyMoney grandtotal ;
MyMoneyFile * file = MyMoneyFile : : instance ( ) ;
result = " " ;
csv = " " ;
result + = TQString ( " <h2 class= \" report \" >%1</h2> \n " ) . arg ( m_config . name ( ) ) ;
csv + = " \" Report: " + m_config . name ( ) + " \" \n " ;
//actual dates of the report
result + = TQString ( " <div class= \" subtitle \" > " ) ;
if ( ! m_config . fromDate ( ) . isNull ( ) ) {
result + = i18n ( " Report date range " , " %1 through %2 " ) . arg ( TDEGlobal : : locale ( ) - > formatDate ( m_config . fromDate ( ) , true ) ) . arg ( TDEGlobal : : locale ( ) - > formatDate ( m_config . toDate ( ) , true ) ) ;
result + = TQString ( " </div> \n " ) ;
result + = TQString ( " <div class= \" gap \" > </div> \n " ) ;
csv + = i18n ( " Report date range " , " %1 through %2 " ) . arg ( TDEGlobal : : locale ( ) - > formatDate ( m_config . fromDate ( ) , true ) ) . arg ( TDEGlobal : : locale ( ) - > formatDate ( m_config . toDate ( ) , true ) ) ;
csv + = TQString ( " \n " ) ;
}
result + = TQString ( " <div class= \" subtitle \" > " ) ;
if ( m_config . isConvertCurrency ( ) )
{
result + = i18n ( " All currencies converted to %1 " ) . arg ( file - > baseCurrency ( ) . name ( ) ) ;
csv + = i18n ( " All currencies converted to %1 \n " ) . arg ( file - > baseCurrency ( ) . name ( ) ) ;
}
else
{
result + = i18n ( " All values shown in %1 unless otherwise noted " ) . arg ( file - > baseCurrency ( ) . name ( ) ) ;
csv + = i18n ( " All values shown in %1 unless otherwise noted \n " ) . arg ( file - > baseCurrency ( ) . name ( ) ) ;
}
result + = TQString ( " </div> \n " ) ;
result + = TQString ( " <div class= \" gap \" > </div> \n " ) ;
// retrieve the configuration parameters from the report definition.
// the things that we care about for query reports are:
// how to group the rows, what columns to display, and what field
// to subtotal on
TQStringList groups = TQStringList : : split ( " , " , m_group ) ;
TQStringList columns = TQStringList : : split ( " , " , m_columns ) ;
columns + = m_subtotal ;
TQStringList postcolumns = TQStringList : : split ( " , " , m_postcolumns ) ;
columns + = postcolumns ;
//
// Table header
//
TQMap < TQString , TQString > i18nHeaders ;
i18nHeaders [ " postdate " ] = i18n ( " Date " ) ;
i18nHeaders [ " value " ] = i18n ( " Amount " ) ;
i18nHeaders [ " number " ] = i18n ( " Num " ) ;
i18nHeaders [ " payee " ] = i18n ( " Payee " ) ;
i18nHeaders [ " category " ] = i18n ( " Category " ) ;
i18nHeaders [ " account " ] = i18n ( " Account " ) ;
i18nHeaders [ " memo " ] = i18n ( " Memo " ) ;
i18nHeaders [ " topcategory " ] = i18n ( " Top Category " ) ;
i18nHeaders [ " categorytype " ] = i18n ( " Category Type " ) ;
i18nHeaders [ " month " ] = i18n ( " Month " ) ;
i18nHeaders [ " week " ] = i18n ( " Week " ) ;
i18nHeaders [ " reconcileflag " ] = i18n ( " Reconciled " ) ;
i18nHeaders [ " action " ] = i18n ( " Action " ) ;
i18nHeaders [ " shares " ] = i18n ( " Shares " ) ;
i18nHeaders [ " price " ] = i18n ( " Price " ) ;
i18nHeaders [ " latestprice " ] = i18n ( " Price " ) ;
i18nHeaders [ " netinvvalue " ] = i18n ( " Net Value " ) ;
i18nHeaders [ " buys " ] = i18n ( " Buys " ) ;
i18nHeaders [ " sells " ] = i18n ( " Sells " ) ;
i18nHeaders [ " reinvestincome " ] = i18n ( " Dividends Reinvested " ) ;
i18nHeaders [ " cashincome " ] = i18n ( " Dividends Paid Out " ) ;
i18nHeaders [ " startingbal " ] = i18n ( " Starting Balance " ) ;
i18nHeaders [ " endingbal " ] = i18n ( " Ending Balance " ) ;
i18nHeaders [ " return " ] = i18n ( " Annualized Return " ) ;
i18nHeaders [ " returninvestment " ] = i18n ( " Return On Investment " ) ;
i18nHeaders [ " fees " ] = i18n ( " Fees " ) ;
i18nHeaders [ " interest " ] = i18n ( " Interest " ) ;
i18nHeaders [ " payment " ] = i18n ( " Payment " ) ;
i18nHeaders [ " balance " ] = i18n ( " Balance " ) ;
i18nHeaders [ " type " ] = i18n ( " Type " ) ;
i18nHeaders [ " name " ] = i18n ( " Name " ) ;
i18nHeaders [ " nextduedate " ] = i18n ( " Next Due Date " ) ;
i18nHeaders [ " occurence " ] = i18n ( " Occurence " ) ;
i18nHeaders [ " paymenttype " ] = i18n ( " Payment Method " ) ;
i18nHeaders [ " institution " ] = i18n ( " Institution " ) ;
i18nHeaders [ " description " ] = i18n ( " Description " ) ;
i18nHeaders [ " openingdate " ] = i18n ( " Opening Date " ) ;
i18nHeaders [ " currencyname " ] = i18n ( " Currency " ) ;
i18nHeaders [ " balancewarning " ] = i18n ( " Balance Early Warning " ) ;
i18nHeaders [ " maxbalancelimit " ] = i18n ( " Balance Max Limit " ) ;
i18nHeaders [ " creditwarning " ] = i18n ( " Credit Early Warning " ) ;
i18nHeaders [ " maxcreditlimit " ] = i18n ( " Credit Max Limit " ) ;
i18nHeaders [ " tax " ] = i18n ( " Tax " ) ;
i18nHeaders [ " favorite " ] = i18n ( " Preferred " ) ;
i18nHeaders [ " loanamount " ] = i18n ( " Loan Amount " ) ;
i18nHeaders [ " interestrate " ] = i18n ( " Interest Rate " ) ;
i18nHeaders [ " nextinterestchange " ] = i18n ( " Next Interest Change " ) ;
i18nHeaders [ " periodicpayment " ] = i18n ( " Periodic Payment " ) ;
i18nHeaders [ " finalpayment " ] = i18n ( " Final Payment " ) ;
i18nHeaders [ " currentbalance " ] = i18n ( " Current Balance " ) ;
// the list of columns which represent money, so we can display them correctly
TQStringList moneyColumns = TQStringList : : split ( " , " , " value,shares,price,latestprice,netinvvalue,buys,sells,cashincome,reinvestincome,startingbal,fees,interest,payment,balance,balancewarning,maxbalancelimit,creditwarning,maxcreditlimit,loanamount,periodicpayment,finalpayment,currentbalance " ) ;
// the list of columns which represent shares, which is like money except the
// transaction currency will not be displayed
TQStringList sharesColumns = TQStringList : : split ( " , " , " shares " ) ;
// the list of columns which represent a percentage, so we can display them correctly
TQStringList percentColumns = TQStringList : : split ( " , " , " return,returninvestment,interestrate " ) ;
// the list of columns which represent dates, so we can display them correctly
TQStringList dateColumns = TQStringList : : split ( " , " , " postdate,entrydate,nextduedate,openingdate,nextinterestchange " ) ;
result + = " <table class= \" report \" > \n <thead><tr class= \" itemheader \" > " ;
TQStringList : : const_iterator it_column = columns . begin ( ) ;
while ( it_column ! = columns . end ( ) )
{
TQString i18nName = i18nHeaders [ * it_column ] ;
if ( i18nName . isEmpty ( ) )
i18nName = * it_column ;
result + = " <th> " + i18nName + " </th> " ;
csv + = i18nName + " , " ;
+ + it_column ;
}
result + = " </tr></thead> \n " ;
csv = csv . left ( csv . length ( ) - 1 ) ;
csv + = " \n " ;
//
// Set up group iterators
//
// There is one active iterator for each level of grouping.
// As we step through the rows
// we update the group iterators each time based on the row data. If
// the group iterator changes and it had a previous value, we print a
// subtotal. Whether or not it had a previous value, we print a group
// header. The group iterator keeps track of a subtotal also.
int depth = 1 ;
TQValueList < GroupIterator > groupIteratorList ;
TQStringList : : const_iterator it_grouplevel = groups . begin ( ) ;
while ( it_grouplevel ! = groups . end ( ) )
{
groupIteratorList + = GroupIterator ( ( * it_grouplevel ) , m_subtotal , depth + + ) ;
+ + it_grouplevel ;
}
//
// Rows
//
bool row_odd = true ;
// ***DV***
MyMoneyMoney startingBalance ;
for ( TQValueList < TableRow > : : const_iterator it_row = m_rows . begin ( ) ;
it_row ! = m_rows . end ( ) ;
+ + it_row ) {
// the standard fraction is the fraction of an non-cash account in the base currency
// this could be overridden using the "fraction" element of a row for each row.
// Currently (2008-02-21) this override is not used at all (ipwizard)
int fraction = file - > baseCurrency ( ) . smallestAccountFraction ( ) ;
if ( ( * it_row ) . find ( " fraction " ) ! = ( * it_row ) . end ( ) )
fraction = ( * it_row ) [ " fraction " ] . toInt ( ) ;
//
// Process Groups
//
// ***DV*** HACK to force a subtotal and header, since this render doesn't
// always detect a group change for different accounts with the same name
// (as occurs with the same stock purchased from different investment accts)
if ( it_row ! = m_rows . begin ( ) )
if ( ( ( * it_row ) [ " rank " ] = = " -2 " ) & & ( ( * it_row ) [ " id " ] = = " A " ) )
( groupIteratorList . last ( ) ) . force ( ) ;
// There's a subtle bug here. If an earlier group gets a new group,
// then we need to force all the downstream groups to get one too.
// Update the group iterators with the current row value
TQValueList < GroupIterator > : : iterator it_group = groupIteratorList . begin ( ) ;
while ( it_group ! = groupIteratorList . end ( ) )
{
( * it_group ) . update ( * it_row ) ;
+ + it_group ;
}
// Do subtotals backwards
if ( m_config . isConvertCurrency ( ) )
{
it_group = groupIteratorList . fromLast ( ) ;
while ( it_group ! = groupIteratorList . end ( ) )
{
if ( ( * it_group ) . isSubtotal ( ) )
{
if ( ( * it_group ) . depth ( ) = = 1 )
grandtotal + = ( * it_group ) . subtotal ( ) ;
grandtotal = grandtotal . convert ( fraction ) ;
TQString subtotal_html = ( * it_group ) . subtotal ( ) . formatMoney ( fraction ) ;
TQString subtotal_csv = ( * it_group ) . subtotal ( ) . formatMoney ( fraction , false ) ;
// ***DV*** HACK fix the side-effiect from .force() method above
TQString oldName = TQString ( ( * it_group ) . oldName ( ) ) . stripWhiteSpace ( ) ;
result + =
" <tr class= \" sectionfooter \" > "
" <td class= \" left " + TQString : : number ( ( ( * it_group ) . depth ( ) - 1 ) ) + " \" "
" colspan= \" " +
TQString : : number ( columns . count ( ) - 1 - postcolumns . count ( ) ) + " \" > " +
i18n ( " Total " ) + " " + oldName + " </td> "
" <td> " + subtotal_html + " </td></tr> \n " ;
csv + =
" \" " + i18n ( " Total " ) + " " + oldName + " \" , \" " + subtotal_csv + " \" \n " ;
}
- - it_group ;
}
}
// And headers forwards
it_group = groupIteratorList . begin ( ) ;
while ( it_group ! = groupIteratorList . end ( ) )
{
if ( ( * it_group ) . isNewHeader ( ) )
{
row_odd = true ;
result + = " <tr class= \" sectionheader \" > "
" <td class= \" left " + TQString : : number ( ( ( * it_group ) . depth ( ) - 1 ) ) + " \" "
" colspan= \" " + TQString : : number ( columns . count ( ) ) + " \" > " +
( * it_group ) . name ( ) + " </td></tr> \n " ;
csv + = " \" " + ( * it_group ) . name ( ) + " \" \n " ;
}
+ + it_group ;
}
//
// Columns
//
// skip the opening and closing balance row,
// if the balance column is not shown
if ( ( columns . contains ( " balance " ) = = 0 ) & & ( ( * it_row ) [ " rank " ] = = " -2 " ) )
continue ;
bool need_label = true ;
// ***DV***
if ( ( * it_row ) [ " rank " ] = = " 0 " ) row_odd = ! row_odd ;
if ( ( * it_row ) [ " rank " ] = = " -2 " )
result + = TQString ( " <tr class= \" item%1 \" > " ) . arg ( ( * it_row ) [ " id " ] ) ;
else
if ( ( * it_row ) [ " rank " ] = = " 1 " )
result + = TQString ( " <tr class= \" %1 \" > " ) . arg ( row_odd ? " item1 " : " item0 " ) ;
else
result + = TQString ( " <tr class= \" %1 \" > " ) . arg ( row_odd ? " row-odd " : " row-even " ) ;
TQStringList : : const_iterator it_column = columns . begin ( ) ;
while ( it_column ! = columns . end ( ) )
{
TQString data = ( * it_row ) [ * it_column ] ;
// ***DV***
if ( ( * it_row ) [ " rank " ] = = " 1 " ) {
if ( * it_column = = " value " )
data = ( * it_row ) [ " split " ] ;
else if ( * it_column = = " postdate "
| | * it_column = = " number "
| | * it_column = = " payee "
| | * it_column = = " action "
| | * it_column = = " shares "
| | * it_column = = " price "
| | * it_column = = " nextduedate "
| | * it_column = = " balance "
| | * it_column = = " account "
| | * it_column = = " name " )
data = " " ;
}
// ***DV***
if ( ( * it_row ) [ " rank " ] = = " -2 " ) {
if ( * it_column = = " balance " ) {
data = ( * it_row ) [ " balance " ] ;
if ( ( * it_row ) [ " id " ] = = " A " ) // opening balance?
startingBalance = MyMoneyMoney ( data ) ;
}
if ( need_label ) {
if ( ( * it_column = = " payee " ) | |
( * it_column = = " category " ) | |
( * it_column = = " memo " ) ) {
if ( ( * it_row ) [ " shares " ] ! = " " ) {
data = ( ( * it_row ) [ " id " ] = = " A " )
? i18n ( " Initial Market Value " )
: i18n ( " Ending Market Value " ) ;
} else {
data = ( ( * it_row ) [ " id " ] = = " A " )
? i18n ( " Opening Balance " )
: i18n ( " Closing Balance " ) ;
}
need_label = false ;
}
}
}
// The 'balance' column is calculated at render-time
// but not printed on split lines
else if ( * it_column = = " balance " & & ( * it_row ) [ " rank " ] = = " 0 " )
{
// Take the balance off the deepest group iterator
data = ( groupIteratorList . back ( ) . currenttotal ( ) + startingBalance ) . toString ( ) ;
}
// Figure out how to render the value in this column, depending on
// what its properties are.
//
// TODO: This and the i18n headings are handled
// as a set of parallel vectors. Would be much better to make a single
// vector of a properties class.
if ( sharesColumns . contains ( * it_column ) )
{
if ( data . isEmpty ( ) ) {
result + = TQString ( " <td></td> " ) ;
csv + = " \" \" , " ;
}
else {
result + = TQString ( " <td>%1</td> " ) . arg ( MyMoneyMoney ( data ) . formatMoney ( " " , 3 ) ) ;
csv + = " \" " + MyMoneyMoney ( data ) . formatMoney ( " " , 3 , false ) + " \" , " ;
}
}
else if ( moneyColumns . contains ( * it_column ) )
{
if ( data . isEmpty ( ) ) {
result + = TQString ( " <td%1></td> " )
. arg ( ( * it_column = = " value " ) ? " class= \" value \" " : " " ) ;
csv + = " \" \" , " ;
} else if ( MyMoneyMoney ( data ) = = MyMoneyMoney : : autoCalc ) {
result + = TQString ( " <td%1>%2</td> " )
. arg ( ( * it_column = = " value " ) ? " class= \" value \" " : " " )
. arg ( i18n ( " Calculated " ) ) ;
csv + = " \" " + i18n ( " Calculated " ) + " \" , " ;
} else if ( * it_column = = " price " ) {
result + = TQString ( " <td>%2</td> " )
. arg ( MyMoneyMoney ( data ) . formatMoney ( MyMoneyMoney : : precToDenom ( KMyMoneyGlobalSettings : : pricePrecision ( ) ) ) ) ;
csv + = " \" " + ( * it_row ) [ " currency " ] + " " + MyMoneyMoney ( data ) . formatMoney ( MyMoneyMoney : : precToDenom ( KMyMoneyGlobalSettings : : pricePrecision ( ) ) , false ) + " \" , " ;
} else {
result + = TQString ( " <td%1>%2 %3</td> " )
. arg ( ( * it_column = = " value " ) ? " class= \" value \" " : " " )
. arg ( ( * it_row ) [ " currency " ] )
. arg ( MyMoneyMoney ( data ) . formatMoney ( fraction ) ) ;
csv + = " \" " + ( * it_row ) [ " currency " ] + " " + MyMoneyMoney ( data ) . formatMoney ( fraction , false ) + " \" , " ;
}
}
else if ( percentColumns . contains ( * it_column ) )
{
data = ( MyMoneyMoney ( data ) * MyMoneyMoney ( 100 , 1 ) ) . formatMoney ( fraction ) ;
result + = TQString ( " <td>%1%</td> " ) . arg ( data ) ;
csv + = data + " %, " ;
}
else if ( dateColumns . contains ( * it_column ) )
{
// do this before we possibly change data
csv + = " \" " + data + " \" , " ;
// if we have a locale() then use its date formatter
if ( TDEGlobal : : locale ( ) & & ! data . isEmpty ( ) ) {
TQDate qd = TQDate : : fromString ( data , Qt : : ISODate ) ;
data = TDEGlobal : : locale ( ) - > formatDate ( qd , true ) ;
}
result + = TQString ( " <td class= \" left \" >%1</td> " ) . arg ( data ) ;
}
else
{
result + = TQString ( " <td class= \" left \" >%1</td> " ) . arg ( data ) ;
csv + = " \" " + data + " \" , " ;
}
+ + it_column ;
}
result + = " </tr> \n " ;
csv = csv . left ( csv . length ( ) - 1 ) ; // remove final comma
csv + = " \n " ;
}
//
// Final group totals
//
// Do subtotals backwards
if ( m_config . isConvertCurrency ( ) )
{
int fraction = file - > baseCurrency ( ) . smallestAccountFraction ( ) ;
TQValueList < GroupIterator > : : iterator it_group = groupIteratorList . fromLast ( ) ;
while ( it_group ! = groupIteratorList . end ( ) )
{
( * it_group ) . update ( TableRow ( ) ) ;
if ( ( * it_group ) . depth ( ) = = 1 ) {
grandtotal + = ( * it_group ) . subtotal ( ) ;
grandtotal = grandtotal . convert ( fraction ) ;
}
TQString subtotal_html = ( * it_group ) . subtotal ( ) . formatMoney ( fraction ) ;
TQString subtotal_csv = ( * it_group ) . subtotal ( ) . formatMoney ( fraction , false ) ;
result + = " <tr class= \" sectionfooter \" > "
" <td class= \" left " + TQString : : number ( ( * it_group ) . depth ( ) - 1 ) + " \" "
" colspan= \" " + TQString : : number ( columns . count ( ) - 1 - postcolumns . count ( ) ) + " \" > " +
i18n ( " Total " ) + " " + ( * it_group ) . oldName ( ) + " </td> "
" <td> " + subtotal_html + " </td></tr> \n " ;
csv + = " \" " + i18n ( " Total " ) + " " + ( * it_group ) . oldName ( ) + " \" , \" " + subtotal_csv + " \" \n " ;
- - it_group ;
}
//
// Grand total
//
TQString grandtotal_html = grandtotal . formatMoney ( fraction ) ;
TQString grandtotal_csv = grandtotal . formatMoney ( fraction , false ) ;
result + = " <tr class= \" sectionfooter \" > "
" <td class= \" left0 \" "
" colspan= \" " + TQString : : number ( columns . count ( ) - 1 - postcolumns . count ( ) ) + " \" > " +
i18n ( " Grand Total " ) + " </td> "
" <td> " + grandtotal_html + " </td></tr> \n " ;
csv + = " \" " + i18n ( " Grand Total " ) + " \" , \" " + grandtotal_csv + " \" \n " ;
}
result + = " </table> \n " ;
}
TQString ListTable : : renderHTML ( void ) const
{
TQString html , csv ;
render ( html , csv ) ;
return html ;
}
TQString ListTable : : renderCSV ( void ) const
{
TQString html , csv ;
render ( html , csv ) ;
return csv ;
}
void ListTable : : dump ( const TQString & file , const TQString & context ) const
{
TQFile g ( file ) ;
g . open ( IO_WriteOnly ) ;
if ( ! context . isEmpty ( ) )
TQTextStream ( & g ) < < context . arg ( renderHTML ( ) ) ;
else
TQTextStream ( & g ) < < renderHTML ( ) ;
g . close ( ) ;
}
void ListTable : : includeInvestmentSubAccounts ( )
{
// if we're not in expert mode, we need to make sure
// that all stock accounts for the selected investment
// account are also selected
TQStringList accountList ;
if ( m_config . accounts ( accountList ) ) {
if ( ! KMyMoneyGlobalSettings : : expertMode ( ) ) {
TQStringList : : const_iterator it_a , it_b ;
for ( it_a = accountList . begin ( ) ; it_a ! = accountList . end ( ) ; + + it_a ) {
MyMoneyAccount acc = MyMoneyFile : : instance ( ) - > account ( * it_a ) ;
if ( acc . accountType ( ) = = MyMoneyAccount : : Investment ) {
for ( it_b = acc . accountList ( ) . begin ( ) ; it_b ! = acc . accountList ( ) . end ( ) ; + + it_b ) {
if ( ! accountList . contains ( * it_b ) ) {
m_config . addAccount ( * it_b ) ;
}
}
}
}
}
}
}
}