/***************************************************************************
pivottable . cpp
- - - - - - - - - - - - - - - - - - -
begin : Mon May 17 2004
copyright : ( C ) 2004 - 2005 by Ace Jones
email : < ace . j @ hotpop . com >
Thomas Baumgart < ipwizard @ users . sourceforge . net >
Alvaro Soliverez < 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 <tqlayout.h>
# include <tqdatetime.h>
# include <tqregexp.h>
# include <tqdragobject.h>
# include <tqclipboard.h>
# include <tqapplication.h>
# include <tqprinter.h>
# include <tqpainter.h>
# include <tqfile.h>
# include <tqdom.h>
// ----------------------------------------------------------------------------
// TDE Includes
// This is just needed for i18n() and weekStartDay().
// Once I figure out how to handle i18n
// without using this macro directly, I'll be freed of KDE dependency. This
// is a minor problem because we use these terms when rendering to HTML,
// and a more major problem because we need it to translate account types
// (e.g. MyMoneyAccount::Checkings) into their text representation. We also
// use that text representation in the core data structure of the report. (Ace)
# include <tdeglobal.h>
# include <tdelocale.h>
# include <kdebug.h>
# include <kcalendarsystem.h>
// ----------------------------------------------------------------------------
// Project Includes
# include "pivottable.h"
# include "pivotgrid.h"
# include "reportdebug.h"
# include "kreportchartview.h"
# include "../kmymoneyglobalsettings.h"
# include "../kmymoneyutils.h"
# include "../mymoney/mymoneyforecast.h"
# include <kmymoney/kmymoneyutils.h>
namespace reports {
TQString Debug : : m_sTabs ;
bool Debug : : m_sEnabled = DEBUG_ENABLED_BY_DEFAULT ;
TQString Debug : : m_sEnableKey ;
Debug : : Debug ( const TQString & _name ) : m_methodName ( _name ) , m_enabled ( m_sEnabled )
{
if ( ! m_enabled & & _name = = m_sEnableKey )
m_enabled = true ;
if ( m_enabled )
{
tqDebug ( TQString ( " %1%2(): ENTER " ) . arg ( m_sTabs ) . arg ( m_methodName ) ) ;
m_sTabs . append ( " -- " ) ;
}
}
Debug : : ~ Debug ( )
{
if ( m_enabled )
{
m_sTabs . remove ( 0 , 2 ) ;
tqDebug ( TQString ( " %1%2(): EXIT " ) . arg ( m_sTabs ) . arg ( m_methodName ) ) ;
if ( m_methodName = = m_sEnableKey )
m_enabled = false ;
}
}
void Debug : : output ( const TQString & _text )
{
if ( m_enabled )
tqDebug ( TQString ( " %1%2(): %3 " ) . arg ( m_sTabs ) . arg ( m_methodName ) . arg ( _text ) ) ;
}
PivotTable : : PivotTable ( const MyMoneyReport & _config_f ) :
m_runningSumsCalculated ( false ) ,
m_config_f ( _config_f )
{
init ( ) ;
}
void PivotTable : : init ( void )
{
DEBUG_ENTER ( __PRETTY_FUNCTION__ ) ;
//
// Initialize locals
//
MyMoneyFile * file = MyMoneyFile : : instance ( ) ;
//
// Initialize member variables
//
//make sure we have all subaccounts of investment accounts
includeInvestmentSubAccounts ( ) ;
m_config_f . validDateRange ( m_beginDate , m_endDate ) ;
// If we need to calculate running sums, it does not make sense
// to show a row total column
if ( m_config_f . isRunningSum ( ) )
m_config_f . setShowingRowTotals ( false ) ;
// if this is a months-based report
if ( ! m_config_f . isColumnsAreDays ( ) )
{
// strip out the 'days' component of the begin and end dates.
// we're only using these variables to contain year and month.
m_beginDate = TQDate ( m_beginDate . year ( ) , m_beginDate . month ( ) , 1 ) ;
m_endDate = TQDate ( m_endDate . year ( ) , m_endDate . month ( ) , 1 ) ;
}
m_numColumns = columnValue ( m_endDate ) - columnValue ( m_beginDate ) + 2 ;
//Load what types of row the report is going to show
loadRowTypeList ( ) ;
//
// Initialize outer groups of the grid
//
if ( m_config_f . rowType ( ) = = MyMoneyReport : : eAssetLiability )
{
m_grid . insert ( KMyMoneyUtils : : accountTypeToString ( MyMoneyAccount : : Asset ) , PivotOuterGroup ( m_numColumns ) ) ;
m_grid . insert ( KMyMoneyUtils : : accountTypeToString ( MyMoneyAccount : : Liability ) , PivotOuterGroup ( m_numColumns , PivotOuterGroup : : m_kDefaultSortOrder , true /* inverted */ ) ) ;
}
else
{
m_grid . insert ( KMyMoneyUtils : : accountTypeToString ( MyMoneyAccount : : Income ) , PivotOuterGroup ( m_numColumns , PivotOuterGroup : : m_kDefaultSortOrder - 2 ) ) ;
m_grid . insert ( KMyMoneyUtils : : accountTypeToString ( MyMoneyAccount : : Expense ) , PivotOuterGroup ( m_numColumns , PivotOuterGroup : : m_kDefaultSortOrder - 1 , true /* inverted */ ) ) ;
//
// Create rows for income/expense reports with all accounts included
//
if ( m_config_f . isIncludingUnusedAccounts ( ) )
createAccountRows ( ) ;
}
//
// Initialize grid totals
//
m_grid . m_total = PivotGridRowSet ( m_numColumns ) ;
//
// Get opening balances
// (for running sum reports only)
//
if ( m_config_f . isRunningSum ( ) )
calculateOpeningBalances ( ) ;
//
// Calculate budget mapping
// (for budget-vs-actual reports only)
//
if ( m_config_f . hasBudget ( ) )
calculateBudgetMapping ( ) ;
//
// Populate all transactions into the row/column pivot grid
//
TQValueList < MyMoneyTransaction > transactions ;
m_config_f . setReportAllSplits ( false ) ;
m_config_f . setConsiderCategory ( true ) ;
try {
transactions = file - > transactionList ( m_config_f ) ;
} catch ( MyMoneyException * e ) {
tqDebug ( TQString ( " ERR: %1 thrown in %2(%s) " ) . arg ( e - > what ( ) ) . arg ( e - > file ( ) ) . arg ( e - > line ( ) ) ) ;
throw e ;
}
DEBUG_OUTPUT ( TQString ( " Found %1 matching transactions " ) . arg ( transactions . count ( ) ) ) ;
// Include scheduled transactions if required
if ( m_config_f . isIncludingSchedules ( ) )
{
// Create a custom version of the report filter, excluding date
// We'll use this to compare the transaction against
MyMoneyTransactionFilter schedulefilter ( m_config_f ) ;
schedulefilter . setDateFilter ( TQDate ( ) , TQDate ( ) ) ;
// Get the real dates from the config filter
TQDate configbegin , configend ;
m_config_f . validDateRange ( configbegin , configend ) ;
TQValueList < MyMoneySchedule > schedules = file - > scheduleList ( ) ;
TQValueList < MyMoneySchedule > : : const_iterator it_schedule = schedules . begin ( ) ;
while ( it_schedule ! = schedules . end ( ) )
{
// If the transaction meets the filter
MyMoneyTransaction tx = ( * it_schedule ) . transaction ( ) ;
if ( ! ( * it_schedule ) . isFinished ( ) & & schedulefilter . match ( tx ) )
{
// Keep the id of the schedule with the transaction so that
// we can do the autocalc later on in case of a loan payment
tx . setValue ( " kmm-schedule-id " , ( * it_schedule ) . id ( ) ) ;
// Get the dates when a payment will be made within the report window
TQDate nextpayment = ( * it_schedule ) . adjustedNextPayment ( configbegin ) ;
if ( nextpayment . isValid ( ) )
{
// Add one transaction for each date
TQValueList < TQDate > paymentDates = ( * it_schedule ) . paymentDates ( nextpayment , configend ) ;
TQValueList < TQDate > : : const_iterator it_date = paymentDates . begin ( ) ;
while ( it_date ! = paymentDates . end ( ) )
{
//if the payment occurs in the past, enter it tomorrow
if ( TQDate : : currentDate ( ) > = * it_date ) {
tx . setPostDate ( TQDate : : currentDate ( ) . addDays ( 1 ) ) ;
} else {
tx . setPostDate ( * it_date ) ;
}
if ( tx . postDate ( ) < = configend
& & tx . postDate ( ) > = configbegin ) {
transactions + = tx ;
}
DEBUG_OUTPUT ( TQString ( " Added transaction for schedule %1 on %2 " ) . arg ( ( * it_schedule ) . id ( ) ) . arg ( ( * it_date ) . toString ( ) ) ) ;
+ + it_date ;
}
}
}
+ + it_schedule ;
}
}
// whether asset & liability transactions are actually to be considered
// transfers
bool al_transfers = ( m_config_f . rowType ( ) = = MyMoneyReport : : eExpenseIncome ) & & ( m_config_f . isIncludingTransfers ( ) ) ;
//this is to store balance for loan accounts when not included in the report
TQMap < TQString , MyMoneyMoney > loanBalances ;
TQValueList < MyMoneyTransaction > : : const_iterator it_transaction = transactions . begin ( ) ;
unsigned colofs = columnValue ( m_beginDate ) - 1 ;
while ( it_transaction ! = transactions . end ( ) )
{
TQDate postdate = ( * it_transaction ) . postDate ( ) ;
unsigned column = columnValue ( postdate ) - colofs ;
MyMoneyTransaction tx = ( * it_transaction ) ;
// check if we need to call the autocalculation routine
if ( tx . isLoanPayment ( ) & & tx . hasAutoCalcSplit ( ) & & ( tx . value ( " kmm-schedule-id " ) . length ( ) > 0 ) ) {
// make sure to consider any autocalculation for loan payments
MyMoneySchedule sched = file - > schedule ( tx . value ( " kmm-schedule-id " ) ) ;
const MyMoneySplit & split = tx . amortizationSplit ( ) ;
if ( ! split . id ( ) . isEmpty ( ) ) {
ReportAccount splitAccount = file - > account ( split . accountId ( ) ) ;
MyMoneyAccount : : accountTypeE type = splitAccount . accountGroup ( ) ;
TQString outergroup = KMyMoneyUtils : : accountTypeToString ( type ) ;
//if the account is included in the report, calculate the balance from the cells
if ( m_config_f . includes ( splitAccount ) ) {
loanBalances [ splitAccount . id ( ) ] = cellBalance ( outergroup , splitAccount , column , false ) ;
} else {
//if it is not in the report and also not in loanBalances, get the balance from the file
if ( ! loanBalances . contains ( splitAccount . id ( ) ) ) {
TQDate dueDate = sched . nextDueDate ( ) ;
//if the payment is overdue, use current date
if ( dueDate < TQDate : : currentDate ( ) )
dueDate = TQDate : : currentDate ( ) ;
//get the balance from the file for the date
loanBalances [ splitAccount . id ( ) ] = file - > balance ( splitAccount . id ( ) , dueDate . addDays ( - 1 ) ) ;
}
}
KMyMoneyUtils : : calculateAutoLoan ( sched , tx , loanBalances ) ;
//if the loan split is not included in the report, update the balance for the next occurrence
if ( ! m_config_f . includes ( splitAccount ) ) {
TQValueList < MyMoneySplit > : : ConstIterator it_loanSplits ;
for ( it_loanSplits = tx . splits ( ) . begin ( ) ; it_loanSplits ! = tx . splits ( ) . end ( ) ; + + it_loanSplits ) {
if ( ( * it_loanSplits ) . isAmortizationSplit ( ) & & ( * it_loanSplits ) . accountId ( ) = = splitAccount . id ( ) )
loanBalances [ splitAccount . id ( ) ] = loanBalances [ splitAccount . id ( ) ] + ( * it_loanSplits ) . shares ( ) ;
}
}
}
}
TQValueList < MyMoneySplit > splits = tx . splits ( ) ;
TQValueList < MyMoneySplit > : : const_iterator it_split = splits . begin ( ) ;
while ( it_split ! = splits . end ( ) )
{
ReportAccount splitAccount = ( * it_split ) . accountId ( ) ;
// Each split must be further filtered, because if even one split matches,
// the ENTIRE transaction is returned with all splits (even non-matching ones)
if ( m_config_f . includes ( splitAccount ) & & m_config_f . match ( & ( * it_split ) ) )
{
// reverse sign to match common notation for cash flow direction, only for expense/income splits
MyMoneyMoney reverse ( splitAccount . isIncomeExpense ( ) ? - 1 : 1 , 1 ) ;
MyMoneyMoney value ;
// the outer group is the account class (major account type)
MyMoneyAccount : : accountTypeE type = splitAccount . accountGroup ( ) ;
TQString outergroup = KMyMoneyUtils : : accountTypeToString ( type ) ;
value = ( * it_split ) . shares ( ) ;
bool stockSplit = tx . isStockSplit ( ) ;
if ( ! stockSplit ) {
// retrieve the value in the account's underlying currency
if ( value ! = MyMoneyMoney : : autoCalc ) {
value = value * reverse ;
} else {
tqDebug ( " PivotTable::PivotTable(): This must not happen " ) ;
value = MyMoneyMoney ( ) ; // keep it 0 so far
}
// Except in the case of transfers on an income/expense report
if ( al_transfers & & ( type = = MyMoneyAccount : : Asset | | type = = MyMoneyAccount : : Liability ) )
{
outergroup = i18n ( " Transfers " ) ;
value = - value ;
}
}
// add the value to its correct position in the pivot table
assignCell ( outergroup , splitAccount , column , value , false , stockSplit ) ;
}
+ + it_split ;
}
+ + it_transaction ;
}
//
// Get forecast data
//
if ( m_config_f . isIncludingForecast ( ) )
calculateForecast ( ) ;
//
//Insert Price data
//
if ( m_config_f . isIncludingPrice ( ) )
fillBasePriceUnit ( ePrice ) ;
//
//Insert Average Price data
//
if ( m_config_f . isIncludingAveragePrice ( ) ) {
fillBasePriceUnit ( eActual ) ;
calculateMovingAverage ( ) ;
}
//
// Collapse columns to match column type
//
if ( m_config_f . columnPitch ( ) > 1 )
collapseColumns ( ) ;
//
// Calculate the running sums
// (for running sum reports only)
//
if ( m_config_f . isRunningSum ( ) )
calculateRunningSums ( ) ;
//
// Calculate Moving Average
//
if ( m_config_f . isIncludingMovingAverage ( ) )
calculateMovingAverage ( ) ;
//
// Calculate Budget Difference
//
if ( m_config_f . isIncludingBudgetActuals ( ) )
calculateBudgetDiff ( ) ;
//
// Convert all values to the deep currency
//
convertToDeepCurrency ( ) ;
//
// Convert all values to the base currency
//
if ( m_config_f . isConvertCurrency ( ) )
convertToBaseCurrency ( ) ;
//
// Determine column headings
//
calculateColumnHeadings ( ) ;
//
// Calculate row and column totals
//
calculateTotals ( ) ;
}
void PivotTable : : collapseColumns ( void )
{
DEBUG_ENTER ( __PRETTY_FUNCTION__ ) ;
unsigned columnpitch = m_config_f . columnPitch ( ) ;
if ( columnpitch ! = 1 )
{
unsigned sourcemonth = ( m_config_f . isColumnsAreDays ( ) )
// use the user's locale to determine the week's start
? ( m_beginDate . dayOfWeek ( ) + 8 - TDEGlobal : : locale ( ) - > weekStartDay ( ) ) % 7
: m_beginDate . month ( ) ;
unsigned sourcecolumn = 1 ;
unsigned destcolumn = 1 ;
while ( sourcecolumn < m_numColumns )
{
if ( sourcecolumn ! = destcolumn )
{
#if 0
// TODO: Clean up this rather inefficient kludge. We really should jump by an entire
// destcolumn at a time on RS reports, and calculate the proper sourcecolumn to use,
// allowing us to clear and accumulate only ONCE per destcolumn
if ( m_config_f . isRunningSum ( ) )
clearColumn ( destcolumn ) ;
# endif
accumulateColumn ( destcolumn , sourcecolumn ) ;
}
if ( + + sourcecolumn < m_numColumns ) {
if ( ( sourcemonth + + % columnpitch ) = = 0 ) {
if ( sourcecolumn ! = + + destcolumn )
clearColumn ( destcolumn ) ;
}
}
}
m_numColumns = destcolumn + 1 ;
}
}
void PivotTable : : accumulateColumn ( unsigned destcolumn , unsigned sourcecolumn )
{
DEBUG_ENTER ( __PRETTY_FUNCTION__ ) ;
DEBUG_OUTPUT ( TQString ( " From Column %1 to %2 " ) . arg ( sourcecolumn ) . arg ( destcolumn ) ) ;
// iterate over outer groups
PivotGrid : : iterator it_outergroup = m_grid . begin ( ) ;
while ( it_outergroup ! = m_grid . end ( ) )
{
// iterate over inner groups
PivotOuterGroup : : iterator it_innergroup = ( * it_outergroup ) . begin ( ) ;
while ( it_innergroup ! = ( * it_outergroup ) . end ( ) )
{
// iterator over rows
PivotInnerGroup : : iterator it_row = ( * it_innergroup ) . begin ( ) ;
while ( it_row ! = ( * it_innergroup ) . end ( ) )
{
if ( ( * it_row ) [ eActual ] . count ( ) < = sourcecolumn )
throw new MYMONEYEXCEPTION ( TQString ( " Sourcecolumn %1 out of grid range (%2) in PivotTable : : accumulateColumn " ).arg(sourcecolumn).arg((*it_row)[eActual].count())) ;
if ( ( * it_row ) [ eActual ] . count ( ) < = destcolumn )
throw new MYMONEYEXCEPTION ( TQString ( " Destcolumn %1 out of grid range (%2) in PivotTable : : accumulateColumn " ).arg(sourcecolumn).arg((*it_row)[eActual].count())) ;
( * it_row ) [ eActual ] [ destcolumn ] + = ( * it_row ) [ eActual ] [ sourcecolumn ] ;
+ + it_row ;
}
+ + it_innergroup ;
}
+ + it_outergroup ;
}
}
void PivotTable : : clearColumn ( unsigned column )
{
DEBUG_ENTER ( __PRETTY_FUNCTION__ ) ;
DEBUG_OUTPUT ( TQString ( " Column %1 " ) . arg ( column ) ) ;
// iterate over outer groups
PivotGrid : : iterator it_outergroup = m_grid . begin ( ) ;
while ( it_outergroup ! = m_grid . end ( ) )
{
// iterate over inner groups
PivotOuterGroup : : iterator it_innergroup = ( * it_outergroup ) . begin ( ) ;
while ( it_innergroup ! = ( * it_outergroup ) . end ( ) )
{
// iterator over rows
PivotInnerGroup : : iterator it_row = ( * it_innergroup ) . begin ( ) ;
while ( it_row ! = ( * it_innergroup ) . end ( ) )
{
if ( ( * it_row ) [ eActual ] . count ( ) < = column )
throw new MYMONEYEXCEPTION ( TQString ( " Column %1 out of grid range (%2) in PivotTable : : accumulateColumn " ).arg(column).arg((*it_row)[eActual].count())) ;
( * it_row + + ) [ eActual ] [ column ] = PivotCell ( ) ;
}
+ + it_innergroup ;
}
+ + it_outergroup ;
}
}
void PivotTable : : calculateColumnHeadings ( void )
{
DEBUG_ENTER ( __PRETTY_FUNCTION__ ) ;
// one column for the opening balance
m_columnHeadings . append ( " Opening " ) ;
unsigned columnpitch = m_config_f . columnPitch ( ) ;
// if this is a days-based report
if ( m_config_f . isColumnsAreDays ( ) )
{
if ( columnpitch = = 1 )
{
TQDate columnDate = m_beginDate ;
unsigned column = 1 ;
while ( column + + < m_numColumns )
{
TQString heading = TDEGlobal : : locale ( ) - > calendar ( ) - > monthName ( columnDate . month ( ) , columnDate . year ( ) , true ) + " " + TQString : : number ( columnDate . day ( ) ) ;
columnDate = columnDate . addDays ( 1 ) ;
m_columnHeadings . append ( heading ) ;
}
}
else
{
TQDate day = m_beginDate ;
TQDate prv = m_beginDate ;
// use the user's locale to determine the week's start
unsigned dow = ( day . dayOfWeek ( ) + 8 - TDEGlobal : : locale ( ) - > weekStartDay ( ) ) % 7 ;
while ( day < = m_endDate )
{
if ( ( ( dow % columnpitch ) = = 0 ) | | ( day = = m_endDate ) )
{
m_columnHeadings . append ( TQString ( " %1 %2 - %3 %4 " )
. arg ( TDEGlobal : : locale ( ) - > calendar ( ) - > monthName ( prv . month ( ) , prv . year ( ) , true ) )
. arg ( prv . day ( ) )
. arg ( TDEGlobal : : locale ( ) - > calendar ( ) - > monthName ( day . month ( ) , day . year ( ) , true ) )
. arg ( day . day ( ) ) ) ;
prv = day . addDays ( 1 ) ;
}
day = day . addDays ( 1 ) ;
dow + + ;
}
}
}
// else it's a months-based report
else
{
if ( columnpitch = = 12 )
{
unsigned year = m_beginDate . year ( ) ;
unsigned column = 1 ;
while ( column + + < m_numColumns )
m_columnHeadings . append ( TQString : : number ( year + + ) ) ;
}
else
{
unsigned year = m_beginDate . year ( ) ;
bool includeyear = ( m_beginDate . year ( ) ! = m_endDate . year ( ) ) ;
unsigned segment = ( m_beginDate . month ( ) - 1 ) / columnpitch ;
unsigned column = 1 ;
while ( column + + < m_numColumns )
{
TQString heading = TDEGlobal : : locale ( ) - > calendar ( ) - > monthName ( 1 + segment * columnpitch , 2000 , true ) ;
if ( columnpitch ! = 1 )
heading + = " - " + TDEGlobal : : locale ( ) - > calendar ( ) - > monthName ( ( 1 + segment ) * columnpitch , 2000 , true ) ;
if ( includeyear )
heading + = " " + TQString : : number ( year ) ;
m_columnHeadings . append ( heading ) ;
if ( + + segment > = 12 / columnpitch )
{
segment - = 12 / columnpitch ;
+ + year ;
}
}
}
}
}
void PivotTable : : createAccountRows ( void )
{
DEBUG_ENTER ( __PRETTY_FUNCTION__ ) ;
MyMoneyFile * file = MyMoneyFile : : instance ( ) ;
TQValueList < MyMoneyAccount > accounts ;
file - > accountList ( accounts ) ;
TQValueList < MyMoneyAccount > : : const_iterator it_account = accounts . begin ( ) ;
while ( it_account ! = accounts . end ( ) )
{
ReportAccount account = * it_account ;
// only include this item if its account group is included in this report
// and if the report includes this account
if ( m_config_f . includes ( * it_account ) )
{
DEBUG_OUTPUT ( TQString ( " Includes account %1 " ) . arg ( account . name ( ) ) ) ;
// the row group is the account class (major account type)
TQString outergroup = KMyMoneyUtils : : accountTypeToString ( account . accountGroup ( ) ) ;
// place into the 'opening' column...
assignCell ( outergroup , account , 0 , MyMoneyMoney ( ) ) ;
}
+ + it_account ;
}
}
void PivotTable : : calculateOpeningBalances ( void )
{
DEBUG_ENTER ( __PRETTY_FUNCTION__ ) ;
// First, determine the inclusive dates of the report. Normally, that's just
// the begin & end dates of m_config_f. However, if either of those dates are
// blank, we need to use m_beginDate and/or m_endDate instead.
TQDate from = m_config_f . fromDate ( ) ;
TQDate to = m_config_f . toDate ( ) ;
if ( ! from . isValid ( ) )
from = m_beginDate ;
if ( ! to . isValid ( ) )
to = m_endDate ;
MyMoneyFile * file = MyMoneyFile : : instance ( ) ;
TQValueList < MyMoneyAccount > accounts ;
file - > accountList ( accounts ) ;
TQValueList < MyMoneyAccount > : : const_iterator it_account = accounts . begin ( ) ;
while ( it_account ! = accounts . end ( ) )
{
ReportAccount account = * it_account ;
// only include this item if its account group is included in this report
// and if the report includes this account
if ( m_config_f . includes ( * it_account ) )
{
//do not include account if it is closed and it has no transactions in the report period
if ( account . isClosed ( ) ) {
//check if the account has transactions for the report timeframe
MyMoneyTransactionFilter filter ;
filter . addAccount ( account . id ( ) ) ;
filter . setDateFilter ( m_beginDate , m_endDate ) ;
filter . setReportAllSplits ( false ) ;
TQValueList < MyMoneyTransaction > transactions = file - > transactionList ( filter ) ;
//if a closed account has no transactions in that timeframe, do not include it
if ( transactions . size ( ) = = 0 ) {
DEBUG_OUTPUT ( TQString ( " DOES NOT INCLUDE account %1 " ) . arg ( account . name ( ) ) ) ;
+ + it_account ;
continue ;
}
}
DEBUG_OUTPUT ( TQString ( " Includes account %1 " ) . arg ( account . name ( ) ) ) ;
// the row group is the account class (major account type)
TQString outergroup = KMyMoneyUtils : : accountTypeToString ( account . accountGroup ( ) ) ;
// extract the balance of the account for the given begin date, which is
// the opening balance plus the sum of all transactions prior to the begin
// date
// this is in the underlying currency
MyMoneyMoney value = file - > balance ( account . id ( ) , from . addDays ( - 1 ) ) ;
// place into the 'opening' column...
assignCell ( outergroup , account , 0 , value ) ;
}
else
{
DEBUG_OUTPUT ( TQString ( " DOES NOT INCLUDE account %1 " ) . arg ( account . name ( ) ) ) ;
}
+ + it_account ;
}
}
void PivotTable : : calculateRunningSums ( PivotInnerGroup : : iterator & it_row )
{
MyMoneyMoney runningsum = it_row . data ( ) [ eActual ] [ 0 ] . calculateRunningSum ( MyMoneyMoney ( 0 , 1 ) ) ;
unsigned column = 1 ;
while ( column < m_numColumns )
{
if ( it_row . data ( ) [ eActual ] . count ( ) < = column )
throw new MYMONEYEXCEPTION ( TQString ( " Column %1 out of grid range (%2) in PivotTable : : calculateRunningSums " ).arg(column).arg(it_row.data()[eActual].count())) ;
runningsum = it_row . data ( ) [ eActual ] [ column ] . calculateRunningSum ( runningsum ) ;
+ + column ;
}
}
void PivotTable : : calculateRunningSums ( void )
{
DEBUG_ENTER ( __PRETTY_FUNCTION__ ) ;
m_runningSumsCalculated = true ;
PivotGrid : : iterator it_outergroup = m_grid . begin ( ) ;
while ( it_outergroup ! = m_grid . end ( ) )
{
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 ( ) )
{
#if 0
MyMoneyMoney runningsum = it_row . data ( ) [ 0 ] ;
unsigned column = 1 ;
while ( column < m_numColumns )
{
if ( it_row . data ( ) [ eActual ] . count ( ) < = column )
throw new MYMONEYEXCEPTION ( TQString ( " Column %1 out of grid range (%2) in PivotTable : : calculateRunningSums " ).arg(column).arg(it_row.data()[eActual].count())) ;
runningsum = ( it_row . data ( ) [ eActual ] [ column ] + = runningsum ) ;
+ + column ;
}
# endif
calculateRunningSums ( it_row ) ;
+ + it_row ;
}
+ + it_innergroup ;
}
+ + it_outergroup ;
}
}
MyMoneyMoney PivotTable : : cellBalance ( const TQString & outergroup , const ReportAccount & _row , unsigned _column , bool budget )
{
if ( m_runningSumsCalculated ) {
tqDebug ( " You must not call PivotTable::cellBalance() after calling PivotTable::calculateRunningSums() " ) ;
throw new MYMONEYEXCEPTION ( TQString ( " You must not call PivotTable::cellBalance() after calling PivotTable : : calculateRunningSums ( ) " )) ;
}
// for budget reports, if this is the actual value, map it to the account which
// holds its budget
ReportAccount row = _row ;
if ( ! budget & & m_config_f . hasBudget ( ) )
{
TQString newrow = m_budgetMap [ row . id ( ) ] ;
// if there was no mapping found, then the budget report is not interested
// in this account.
if ( newrow . isEmpty ( ) )
return MyMoneyMoney ( ) ;
row = newrow ;
}
// ensure the row already exists (and its parental hierarchy)
createRow ( outergroup , row , true ) ;
// Determine the inner group from the top-most parent account
TQString innergroup ( row . topParentName ( ) ) ;
if ( m_numColumns < = _column )
throw new MYMONEYEXCEPTION ( TQString ( " Column %1 out of m_numColumns range (%2) in PivotTable : : cellBalance " ).arg(_column).arg(m_numColumns)) ;
if ( m_grid [ outergroup ] [ innergroup ] [ row ] [ eActual ] . count ( ) < = _column )
throw new MYMONEYEXCEPTION ( TQString ( " Column %1 out of grid range (%2) in PivotTable : : cellBalance " ).arg(_column).arg(m_grid[outergroup][innergroup][row][eActual].count())) ;
MyMoneyMoney balance ;
if ( budget )
balance = m_grid [ outergroup ] [ innergroup ] [ row ] [ eBudget ] [ 0 ] . cellBalance ( MyMoneyMoney ( ) ) ;
else
balance = m_grid [ outergroup ] [ innergroup ] [ row ] [ eActual ] [ 0 ] . cellBalance ( MyMoneyMoney ( ) ) ;
unsigned column = 1 ;
while ( column < _column )
{
if ( m_grid [ outergroup ] [ innergroup ] [ row ] [ eActual ] . count ( ) < = column )
throw new MYMONEYEXCEPTION ( TQString ( " Column %1 out of grid range (%2) in PivotTable : : cellBalance " ).arg(column).arg(m_grid[outergroup][innergroup][row][eActual].count())) ;
balance = m_grid [ outergroup ] [ innergroup ] [ row ] [ eActual ] [ column ] . cellBalance ( balance ) ;
+ + column ;
}
return balance ;
}
void PivotTable : : calculateBudgetMapping ( void )
{
DEBUG_ENTER ( __PRETTY_FUNCTION__ ) ;
MyMoneyFile * file = MyMoneyFile : : instance ( ) ;
// Only do this if there is at least one budget in the file
if ( file - > countBudgets ( ) )
{
// Select a budget
//
// It will choose the first budget in the list for the start year of the report if no budget is select
MyMoneyBudget budget = MyMoneyBudget ( ) ;
TQValueList < MyMoneyBudget > budgets = file - > budgetList ( ) ;
bool validBudget = false ;
//check that the selected budget is valid
if ( m_config_f . budget ( ) ! = " Any " ) {
TQValueList < MyMoneyBudget > : : const_iterator budgets_it = budgets . begin ( ) ;
while ( budgets_it ! = budgets . end ( ) ) {
//pick the budget by id
if ( ( * budgets_it ) . id ( ) = = m_config_f . budget ( ) ) {
budget = file - > budget ( ( * budgets_it ) . id ( ) ) ;
validBudget = true ;
break ;
}
+ + budgets_it ;
}
}
//if no budget has been selected
if ( ! validBudget ) {
TQValueList < MyMoneyBudget > : : const_iterator budgets_it = budgets . begin ( ) ;
while ( budgets_it ! = budgets . end ( ) ) {
//pick the first budget that matches the report start year
if ( ( * budgets_it ) . budgetStart ( ) . year ( ) = = TQDate : : currentDate ( ) . year ( ) ) {
budget = file - > budget ( ( * budgets_it ) . id ( ) ) ;
break ;
}
+ + budgets_it ;
}
//if we can't find a matching budget, take the first of the list
if ( budget . id ( ) = = " " )
budget = budgets [ 0 ] ;
//assign the budget to the report
m_config_f . setBudget ( budget . id ( ) , m_config_f . isIncludingBudgetActuals ( ) ) ;
}
// Dump the budget
//kdDebug(2) << "Budget " << budget.name() << ": " << endl;
// Go through all accounts in the system to build the mapping
TQValueList < MyMoneyAccount > accounts ;
file - > accountList ( accounts ) ;
TQValueList < MyMoneyAccount > : : const_iterator it_account = accounts . begin ( ) ;
while ( it_account ! = accounts . end ( ) )
{
//include only the accounts selected for the report
if ( m_config_f . includes ( * it_account ) ) {
TQString id = ( * it_account ) . id ( ) ;
TQString acid = id ;
// If the budget contains this account outright
if ( budget . contains ( id ) )
{
// Add it to the mapping
m_budgetMap [ acid ] = id ;
// kdDebug(2) << ReportAccount(acid).debugName() << " self-maps / type =" << budget.account(id).budgetLevel() << endl;
}
// Otherwise, search for a parent account which includes sub-accounts
else
{
//if includeBudgetActuals, include all accounts regardless of whether in budget or not
if ( m_config_f . isIncludingBudgetActuals ( ) ) {
m_budgetMap [ acid ] = id ;
// kdDebug(2) << ReportAccount(acid).debugName() << " maps to " << ReportAccount(id).debugName() << endl;
}
do
{
id = file - > account ( id ) . parentAccountId ( ) ;
if ( budget . contains ( id ) )
{
if ( budget . account ( id ) . budgetSubaccounts ( ) )
{
m_budgetMap [ acid ] = id ;
// kdDebug(2) << ReportAccount(acid).debugName() << " maps to " << ReportAccount(id).debugName() << endl;
break ;
}
}
}
while ( ! id . isEmpty ( ) ) ;
}
}
+ + it_account ;
} // end while looping through the accounts in the file
// Place the budget values into the budget grid
TQValueList < MyMoneyBudget : : AccountGroup > baccounts = budget . getaccounts ( ) ;
TQValueList < MyMoneyBudget : : AccountGroup > : : const_iterator it_bacc = baccounts . begin ( ) ;
while ( it_bacc ! = baccounts . end ( ) )
{
ReportAccount splitAccount = ( * it_bacc ) . id ( ) ;
//include the budget account only if it is included in the report
if ( m_config_f . includes ( splitAccount ) ) {
MyMoneyAccount : : accountTypeE type = splitAccount . accountGroup ( ) ;
TQString outergroup = KMyMoneyUtils : : accountTypeToString ( type ) ;
// reverse sign to match common notation for cash flow direction, only for expense/income splits
MyMoneyMoney reverse ( ( splitAccount . accountType ( ) = = MyMoneyAccount : : Expense ) ? - 1 : 1 , 1 ) ;
const TQMap < TQDate , MyMoneyBudget : : PeriodGroup > & periods = ( * it_bacc ) . getPeriods ( ) ;
MyMoneyMoney value = ( * periods . begin ( ) ) . amount ( ) * reverse ;
MyMoneyMoney price = MyMoneyMoney ( 1 , 1 ) ;
unsigned column = 1 ;
// based on the kind of budget it is, deal accordingly
switch ( ( * it_bacc ) . budgetLevel ( ) )
{
case MyMoneyBudget : : AccountGroup : : eYearly :
// divide the single yearly value by 12 and place it in each column
value / = MyMoneyMoney ( 12 , 1 ) ;
case MyMoneyBudget : : AccountGroup : : eNone :
case MyMoneyBudget : : AccountGroup : : eMax :
case MyMoneyBudget : : AccountGroup : : eMonthly :
// place the single monthly value in each column of the report
// only add the value if columns are monthly or longer
if ( m_config_f . columnType ( ) = = MyMoneyReport : : eBiMonths
| | m_config_f . columnType ( ) = = MyMoneyReport : : eMonths
| | m_config_f . columnType ( ) = = MyMoneyReport : : eYears
| | m_config_f . columnType ( ) = = MyMoneyReport : : eQuarters ) {
//value = value * MyMoneyMoney(m_config_f.columnType(), 1);
TQDate budgetDate = budget . budgetStart ( ) ;
while ( column < m_numColumns & & budget . budgetStart ( ) . addYears ( 1 ) > budgetDate ) {
//only show budget values if the budget year and the column date match
//no currency conversion is done here because that is done for all columns later
if ( budgetDate > columnDate ( column ) ) {
+ + column ;
} else {
if ( budgetDate > = m_beginDate . addDays ( - m_beginDate . day ( ) + 1 )
& & budgetDate < = m_endDate . addDays ( m_endDate . daysInMonth ( ) - m_endDate . day ( ) )
& & budgetDate > ( columnDate ( column ) . addMonths ( - m_config_f . columnType ( ) ) ) ) {
assignCell ( outergroup , splitAccount , column , value , true /*budget*/ ) ;
}
budgetDate = budgetDate . addMonths ( 1 ) ;
}
}
}
break ;
case MyMoneyBudget : : AccountGroup : : eMonthByMonth :
// place each value in the appropriate column
// budget periods are supposed to come in order just like columns
{
TQMap < TQDate , MyMoneyBudget : : PeriodGroup > : : const_iterator it_period = periods . begin ( ) ;
while ( it_period ! = periods . end ( ) & & column < m_numColumns )
{
if ( ( * it_period ) . startDate ( ) > columnDate ( column ) ) {
+ + column ;
} else {
switch ( m_config_f . columnType ( ) ) {
case MyMoneyReport : : eYears :
case MyMoneyReport : : eBiMonths :
case MyMoneyReport : : eQuarters :
case MyMoneyReport : : eMonths :
{
if ( ( * it_period ) . startDate ( ) > = m_beginDate . addDays ( - m_beginDate . day ( ) + 1 )
& & ( * it_period ) . startDate ( ) < = m_endDate . addDays ( m_endDate . daysInMonth ( ) - m_endDate . day ( ) )
& & ( * it_period ) . startDate ( ) > ( columnDate ( column ) . addMonths ( - m_config_f . columnType ( ) ) ) ) {
//no currency conversion is done here because that is done for all columns later
value = ( * it_period ) . amount ( ) * reverse ;
assignCell ( outergroup , splitAccount , column , value , true /*budget*/ ) ;
}
+ + it_period ;
break ;
}
default :
break ;
}
}
}
break ;
}
}
}
+ + it_bacc ;
}
} // end if there was a budget
}
void PivotTable : : convertToBaseCurrency ( void )
{
DEBUG_ENTER ( __PRETTY_FUNCTION__ ) ;
int fraction = MyMoneyFile : : instance ( ) - > baseCurrency ( ) . smallestAccountFraction ( ) ;
PivotGrid : : iterator it_outergroup = m_grid . begin ( ) ;
while ( it_outergroup ! = m_grid . end ( ) )
{
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 ( ) )
{
unsigned column = 1 ;
while ( column < m_numColumns )
{
if ( it_row . data ( ) [ eActual ] . count ( ) < = column )
throw new MYMONEYEXCEPTION ( TQString ( " Column %1 out of grid range (%2) in PivotTable : : convertToBaseCurrency " ).arg(column).arg(it_row.data()[eActual].count())) ;
TQDate valuedate = columnDate ( column ) ;
//get base price for that date
MyMoneyMoney conversionfactor = it_row . key ( ) . baseCurrencyPrice ( valuedate ) ;
for ( unsigned i = 0 ; i < m_rowTypeList . size ( ) ; + + i ) {
if ( m_rowTypeList [ i ] ! = eAverage ) {
//calculate base value
MyMoneyMoney oldval = it_row . data ( ) [ m_rowTypeList [ i ] ] [ column ] ;
MyMoneyMoney value = ( oldval * conversionfactor ) . reduce ( ) ;
//convert to lowest fraction
it_row . data ( ) [ m_rowTypeList [ i ] ] [ column ] = PivotCell ( value . convert ( fraction ) ) ;
DEBUG_OUTPUT_IF ( conversionfactor ! = MyMoneyMoney ( 1 , 1 ) , TQString ( " Factor of %1, value was %2, now %3 " ) . arg ( conversionfactor ) . arg ( DEBUG_SENSITIVE ( oldval ) ) . arg ( DEBUG_SENSITIVE ( it_row . data ( ) [ m_rowTypeList [ i ] ] [ column ] . toDouble ( ) ) ) ) ;
}
}
+ + column ;
}
+ + it_row ;
}
+ + it_innergroup ;
}
+ + it_outergroup ;
}
}
void PivotTable : : convertToDeepCurrency ( void )
{
DEBUG_ENTER ( __PRETTY_FUNCTION__ ) ;
MyMoneyFile * file = MyMoneyFile : : instance ( ) ;
PivotGrid : : iterator it_outergroup = m_grid . begin ( ) ;
while ( it_outergroup ! = m_grid . end ( ) )
{
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 ( ) )
{
unsigned column = 1 ;
while ( column < m_numColumns )
{
if ( it_row . data ( ) [ eActual ] . count ( ) < = column )
throw new MYMONEYEXCEPTION ( TQString ( " Column %1 out of grid range (%2) in PivotTable : : convertToDeepCurrency " ).arg(column).arg(it_row.data()[eActual].count())) ;
TQDate valuedate = columnDate ( column ) ;
//get conversion factor for the account and date
MyMoneyMoney conversionfactor = it_row . key ( ) . deepCurrencyPrice ( valuedate ) ;
//use the fraction relevant to the account at hand
int fraction = it_row . key ( ) . currency ( ) . smallestAccountFraction ( ) ;
//use base currency fraction if not initialized
if ( fraction = = - 1 )
fraction = file - > baseCurrency ( ) . smallestAccountFraction ( ) ;
//convert to deep currency
MyMoneyMoney oldval = it_row . data ( ) [ eActual ] [ column ] ;
MyMoneyMoney value = ( oldval * conversionfactor ) . reduce ( ) ;
//reduce to lowest fraction
it_row . data ( ) [ eActual ] [ column ] = PivotCell ( value . convert ( fraction ) ) ;
//convert price data
if ( m_config_f . isIncludingPrice ( ) ) {
MyMoneyMoney oldPriceVal = it_row . data ( ) [ ePrice ] [ column ] ;
MyMoneyMoney priceValue = ( oldPriceVal * conversionfactor ) . reduce ( ) ;
it_row . data ( ) [ ePrice ] [ column ] = PivotCell ( priceValue . convert ( 10000 ) ) ;
}
DEBUG_OUTPUT_IF ( conversionfactor ! = MyMoneyMoney ( 1 , 1 ) , TQString ( " Factor of %1, value was %2, now %3 " ) . arg ( conversionfactor ) . arg ( DEBUG_SENSITIVE ( oldval ) ) . arg ( DEBUG_SENSITIVE ( it_row . data ( ) [ eActual ] [ column ] . toDouble ( ) ) ) ) ;
+ + column ;
}
+ + it_row ;
}
+ + it_innergroup ;
}
+ + it_outergroup ;
}
}
void PivotTable : : calculateTotals ( void )
{
//insert the row type that is going to be used
for ( unsigned i = 0 ; i < m_rowTypeList . size ( ) ; + + i )
m_grid . m_total [ m_rowTypeList [ i ] ] . insert ( m_grid . m_total [ m_rowTypeList [ i ] ] . end ( ) , m_numColumns , PivotCell ( ) ) ;
//
// Outer groups
//
// iterate over outer groups
PivotGrid : : iterator it_outergroup = m_grid . begin ( ) ;
while ( it_outergroup ! = m_grid . end ( ) )
{
for ( unsigned i = 0 ; i < m_rowTypeList . size ( ) ; + + i )
( * it_outergroup ) . m_total [ m_rowTypeList [ i ] ] . insert ( ( * it_outergroup ) . m_total [ m_rowTypeList [ i ] ] . end ( ) , m_numColumns , PivotCell ( ) ) ;
//
// Inner Groups
//
PivotOuterGroup : : iterator it_innergroup = ( * it_outergroup ) . begin ( ) ;
while ( it_innergroup ! = ( * it_outergroup ) . end ( ) )
{
for ( unsigned i = 0 ; i < m_rowTypeList . size ( ) ; + + i )
( * it_innergroup ) . m_total [ m_rowTypeList [ i ] ] . insert ( ( * it_innergroup ) . m_total [ m_rowTypeList [ i ] ] . end ( ) , m_numColumns , PivotCell ( ) ) ;
//
// Rows
//
PivotInnerGroup : : iterator it_row = ( * it_innergroup ) . begin ( ) ;
while ( it_row ! = ( * it_innergroup ) . end ( ) )
{
//
// Columns
//
unsigned column = 1 ;
while ( column < m_numColumns )
{
for ( unsigned i = 0 ; i < m_rowTypeList . size ( ) ; + + i ) {
if ( it_row . data ( ) [ m_rowTypeList [ i ] ] . count ( ) < = column )
throw new MYMONEYEXCEPTION ( TQString ( " Column %1 out of grid range (%2) in PivotTable : : calculateTotals , row columns " ).arg(column).arg(it_row.data()[ m_rowTypeList[i] ].count())) ;
if ( ( * it_innergroup ) . m_total [ m_rowTypeList [ i ] ] . count ( ) < = column )
throw new MYMONEYEXCEPTION ( TQString ( " Column %1 out of grid range (%2) in PivotTable : : calculateTotals , inner group totals " ).arg(column).arg((*it_innergroup).m_total[ m_rowTypeList[i] ].count())) ;
//calculate total
MyMoneyMoney value = it_row . data ( ) [ m_rowTypeList [ i ] ] [ column ] ;
( * it_innergroup ) . m_total [ m_rowTypeList [ i ] ] [ column ] + = value ;
( * it_row ) [ m_rowTypeList [ i ] ] . m_total + = value ;
}
+ + column ;
}
+ + it_row ;
}
//
// Inner Row Group Totals
//
unsigned column = 1 ;
while ( column < m_numColumns )
{
for ( unsigned i = 0 ; i < m_rowTypeList . size ( ) ; + + i ) {
if ( ( * it_innergroup ) . m_total [ m_rowTypeList [ i ] ] . count ( ) < = column )
throw new MYMONEYEXCEPTION ( TQString ( " Column %1 out of grid range (%2) in PivotTable : : calculateTotals , inner group totals " ).arg(column).arg((*it_innergroup).m_total[ m_rowTypeList[i] ].count())) ;
if ( ( * it_outergroup ) . m_total [ m_rowTypeList [ i ] ] . count ( ) < = column )
throw new MYMONEYEXCEPTION ( TQString ( " Column %1 out of grid range (%2) in PivotTable : : calculateTotals , outer group totals " ).arg(column).arg((*it_innergroup).m_total[ m_rowTypeList[i] ].count())) ;
//calculate totals
MyMoneyMoney value = ( * it_innergroup ) . m_total [ m_rowTypeList [ i ] ] [ column ] ;
( * it_outergroup ) . m_total [ m_rowTypeList [ i ] ] [ column ] + = value ;
( * it_innergroup ) . m_total [ m_rowTypeList [ i ] ] . m_total + = value ;
}
+ + column ;
}
+ + it_innergroup ;
}
//
// Outer Row Group Totals
//
bool invert_total = ( * it_outergroup ) . m_inverted ;
unsigned column = 1 ;
while ( column < m_numColumns )
{
for ( unsigned i = 0 ; i < m_rowTypeList . size ( ) ; + + i ) {
if ( m_grid . m_total [ m_rowTypeList [ i ] ] . count ( ) < = column )
throw new MYMONEYEXCEPTION ( TQString ( " Column %1 out of grid range (%2) in PivotTable : : calculateTotals , grid totals " ).arg(column).arg((*it_innergroup).m_total[ m_rowTypeList[i] ].count())) ;
//calculate actual totals
MyMoneyMoney value = ( * it_outergroup ) . m_total [ m_rowTypeList [ i ] ] [ column ] ;
( * it_outergroup ) . m_total [ m_rowTypeList [ i ] ] . m_total + = value ;
//so far the invert only applies to actual and budget
if ( invert_total
& & m_rowTypeList [ i ] ! = eBudgetDiff
& & m_rowTypeList [ i ] ! = eForecast )
value = - value ;
m_grid . m_total [ m_rowTypeList [ i ] ] [ column ] + = value ;
}
+ + column ;
}
+ + it_outergroup ;
}
//
// Report Totals
//
unsigned totalcolumn = 1 ;
while ( totalcolumn < m_numColumns )
{
for ( unsigned i = 0 ; i < m_rowTypeList . size ( ) ; + + i ) {
if ( m_grid . m_total [ m_rowTypeList [ i ] ] . count ( ) < = totalcolumn )
throw new MYMONEYEXCEPTION ( TQString ( " Total column %1 out of grid range (%2) in PivotTable : : calculateTotals , grid totals " ).arg(totalcolumn).arg(m_grid.m_total[ m_rowTypeList[i] ].count())) ;
//calculate actual totals
MyMoneyMoney value = m_grid . m_total [ m_rowTypeList [ i ] ] [ totalcolumn ] ;
m_grid . m_total [ m_rowTypeList [ i ] ] . m_total + = value ;
}
+ + totalcolumn ;
}
}
void PivotTable : : assignCell ( const TQString & outergroup , const ReportAccount & _row , unsigned column , MyMoneyMoney value , bool budget , bool stockSplit )
{
DEBUG_ENTER ( __PRETTY_FUNCTION__ ) ;
DEBUG_OUTPUT ( TQString ( " Parameters: %1,%2,%3,%4,%5 " ) . arg ( outergroup ) . arg ( _row . debugName ( ) ) . arg ( column ) . arg ( DEBUG_SENSITIVE ( value . toDouble ( ) ) ) . arg ( budget ) ) ;
// for budget reports, if this is the actual value, map it to the account which
// holds its budget
ReportAccount row = _row ;
if ( ! budget & & m_config_f . hasBudget ( ) )
{
TQString newrow = m_budgetMap [ row . id ( ) ] ;
// if there was no mapping found, then the budget report is not interested
// in this account.
if ( newrow . isEmpty ( ) )
return ;
row = newrow ;
}
// ensure the row already exists (and its parental hierarchy)
createRow ( outergroup , row , true ) ;
// Determine the inner group from the top-most parent account
TQString innergroup ( row . topParentName ( ) ) ;
if ( m_numColumns < = column )
throw new MYMONEYEXCEPTION ( TQString ( " Column %1 out of m_numColumns range (%2) in PivotTable : : assignCell " ).arg(column).arg(m_numColumns)) ;
if ( m_grid [ outergroup ] [ innergroup ] [ row ] [ eActual ] . count ( ) < = column )
throw new MYMONEYEXCEPTION ( TQString ( " Column %1 out of grid range (%2) in PivotTable : : assignCell " ).arg(column).arg(m_grid[outergroup][innergroup][row][eActual].count())) ;
if ( ! stockSplit ) {
// Determine whether the value should be inverted before being placed in the row
if ( m_grid [ outergroup ] . m_inverted )
value = - value ;
// Add the value to the grid cell
if ( budget )
m_grid [ outergroup ] [ innergroup ] [ row ] [ eBudget ] [ column ] + = value ;
else
m_grid [ outergroup ] [ innergroup ] [ row ] [ eActual ] [ column ] + = value ;
} else {
m_grid [ outergroup ] [ innergroup ] [ row ] [ eActual ] [ column ] + = PivotCell : : stockSplit ( value ) ;
}
}
void PivotTable : : createRow ( const TQString & outergroup , const ReportAccount & row , bool recursive )
{
DEBUG_ENTER ( __PRETTY_FUNCTION__ ) ;
// Determine the inner group from the top-most parent account
TQString innergroup ( row . topParentName ( ) ) ;
if ( ! m_grid . contains ( outergroup ) )
{
DEBUG_OUTPUT ( TQString ( " Adding group [%1] " ) . arg ( outergroup ) ) ;
m_grid [ outergroup ] = PivotOuterGroup ( m_numColumns ) ;
}
if ( ! m_grid [ outergroup ] . contains ( innergroup ) )
{
DEBUG_OUTPUT ( TQString ( " Adding group [%1][%2] " ) . arg ( outergroup ) . arg ( innergroup ) ) ;
m_grid [ outergroup ] [ innergroup ] = PivotInnerGroup ( m_numColumns ) ;
}
if ( ! m_grid [ outergroup ] [ innergroup ] . contains ( row ) )
{
DEBUG_OUTPUT ( TQString ( " Adding row [%1][%2][%3] " ) . arg ( outergroup ) . arg ( innergroup ) . arg ( row . debugName ( ) ) ) ;
m_grid [ outergroup ] [ innergroup ] [ row ] = PivotGridRowSet ( m_numColumns ) ;
if ( recursive & & ! row . isTopLevel ( ) )
createRow ( outergroup , row . parent ( ) , recursive ) ;
}
}
unsigned PivotTable : : columnValue ( const TQDate & _date ) const
{
if ( m_config_f . isColumnsAreDays ( ) )
return ( TQDate ( ) . daysTo ( _date ) ) ;
else
return ( _date . year ( ) * 12 + _date . month ( ) ) ;
}
TQDate PivotTable : : columnDate ( int column ) const
{
if ( m_config_f . isColumnsAreDays ( ) )
return m_beginDate . addDays ( m_config_f . columnPitch ( ) * column - 1 ) ;
else
return m_beginDate . addMonths ( m_config_f . columnPitch ( ) * column ) . addDays ( - 1 ) ;
}
TQString PivotTable : : renderCSV ( void ) const
{
DEBUG_ENTER ( __PRETTY_FUNCTION__ ) ;
//
// Report Title
//
TQString result = TQString ( " \" Report: %1 \" \n " ) . arg ( m_config_f . name ( ) ) ;
if ( m_config_f . isConvertCurrency ( ) )
result + = i18n ( " All currencies converted to %1 \n " ) . arg ( MyMoneyFile : : instance ( ) - > baseCurrency ( ) . name ( ) ) ;
else
result + = i18n ( " All values shown in %1 unless otherwise noted \n " ) . arg ( MyMoneyFile : : instance ( ) - > baseCurrency ( ) . name ( ) ) ;
//
// Table Header
//
result + = i18n ( " Account " ) ;
unsigned column = 1 ;
while ( column < m_numColumns )
result + = TQString ( " ,%1 " ) . arg ( TQString ( m_columnHeadings [ column + + ] ) ) ;
if ( m_config_f . isShowingRowTotals ( ) )
result + = TQString ( " ,%1 " ) . arg ( i18n ( " Total " ) ) ;
result + = " \n " ;
int fraction = MyMoneyFile : : instance ( ) - > baseCurrency ( ) . smallestAccountFraction ( ) ;
//
// Outer groups
//
// iterate over outer groups
PivotGrid : : const_iterator it_outergroup = m_grid . begin ( ) ;
while ( it_outergroup ! = m_grid . end ( ) )
{
//
// Outer Group Header
//
result + = it_outergroup . key ( ) + " \n " ;
//
// Inner Groups
//
PivotOuterGroup : : const_iterator it_innergroup = ( * it_outergroup ) . begin ( ) ;
unsigned rownum = 0 ;
while ( it_innergroup ! = ( * it_outergroup ) . end ( ) )
{
//
// Rows
//
TQString innergroupdata ;
PivotInnerGroup : : const_iterator it_row = ( * it_innergroup ) . begin ( ) ;
while ( it_row ! = ( * it_innergroup ) . end ( ) )
{
ReportAccount rowname = it_row . key ( ) ;
int fraction = rowname . currency ( ) . smallestAccountFraction ( ) ;
//
// Columns
//
TQString rowdata ;
unsigned column = 1 ;
bool isUsed = false ;
for ( unsigned i = 0 ; i < m_rowTypeList . size ( ) ; + + i )
isUsed | = it_row . data ( ) [ m_rowTypeList [ i ] ] [ 0 ] . isUsed ( ) ;
while ( column < m_numColumns ) {
//show columns
for ( unsigned i = 0 ; i < m_rowTypeList . size ( ) ; + + i ) {
isUsed | = it_row . data ( ) [ m_rowTypeList [ i ] ] [ column ] . isUsed ( ) ;
rowdata + = TQString ( " , \" %1 \" " ) . arg ( it_row . data ( ) [ m_rowTypeList [ i ] ] [ column ] . formatMoney ( fraction , false ) ) ;
}
column + + ;
}
if ( m_config_f . isShowingRowTotals ( ) ) {
for ( unsigned i = 0 ; i < m_rowTypeList . size ( ) ; + + i )
rowdata + = TQString ( " , \" %1 \" " ) . arg ( ( * it_row ) [ m_rowTypeList [ i ] ] . m_total . formatMoney ( fraction , false ) ) ;
}
//
// Row Header
//
if ( ! rowname . isClosed ( ) | | isUsed ) {
innergroupdata + = " \" " + TQString ( ) . fill ( ' ' , rowname . hierarchyDepth ( ) - 1 ) + rowname . name ( ) ;
// if we don't convert the currencies to the base currency and the
// current row contains a foreign currency, then we append the currency
// to the name of the account
if ( ! m_config_f . isConvertCurrency ( ) & & rowname . isForeignCurrency ( ) )
innergroupdata + = TQString ( " (%1) " ) . arg ( rowname . currencyId ( ) ) ;
innergroupdata + = " \" " ;
if ( isUsed )
innergroupdata + = rowdata ;
innergroupdata + = " \n " ;
}
+ + it_row ;
}
//
// Inner Row Group Totals
//
bool finishrow = true ;
TQString finalRow ;
bool isUsed = false ;
if ( m_config_f . detailLevel ( ) = = MyMoneyReport : : eDetailAll & & ( ( * it_innergroup ) . size ( ) > 1 ) )
{
// Print the individual rows
result + = innergroupdata ;
if ( m_config_f . isShowingColumnTotals ( ) )
{
// Start the TOTALS row
finalRow = i18n ( " Total " ) ;
isUsed = true ;
}
else
{
+ + rownum ;
finishrow = false ;
}
}
else
{
// Start the single INDIVIDUAL ACCOUNT row
ReportAccount rowname = ( * it_innergroup ) . begin ( ) . key ( ) ;
isUsed | = ! rowname . isClosed ( ) ;
finalRow = " \" " + TQString ( ) . fill ( ' ' , rowname . hierarchyDepth ( ) - 1 ) + rowname . name ( ) ;
if ( ! m_config_f . isConvertCurrency ( ) & & rowname . isForeignCurrency ( ) )
finalRow + = TQString ( " (%1) " ) . arg ( rowname . currencyId ( ) ) ;
finalRow + = " \" " ;
}
// Finish the row started above, unless told not to
if ( finishrow )
{
unsigned column = 1 ;
for ( unsigned i = 0 ; i < m_rowTypeList . size ( ) ; + + i )
isUsed | = ( * it_innergroup ) . m_total [ m_rowTypeList [ i ] ] [ 0 ] . isUsed ( ) ;
while ( column < m_numColumns )
{
for ( unsigned i = 0 ; i < m_rowTypeList . size ( ) ; + + i ) {
isUsed | = ( * it_innergroup ) . m_total [ m_rowTypeList [ i ] ] [ column ] . isUsed ( ) ;
finalRow + = TQString ( " , \" %1 \" " ) . arg ( ( * it_innergroup ) . m_total [ m_rowTypeList [ i ] ] [ column ] . formatMoney ( fraction , false ) ) ;
}
column + + ;
}
if ( m_config_f . isShowingRowTotals ( ) ) {
for ( unsigned i = 0 ; i < m_rowTypeList . size ( ) ; + + i )
finalRow + = TQString ( " , \" %1 \" " ) . arg ( ( * it_innergroup ) . m_total [ m_rowTypeList [ i ] ] . m_total . formatMoney ( fraction , false ) ) ;
}
finalRow + = " \n " ;
}
if ( isUsed )
{
result + = finalRow ;
+ + rownum ;
}
+ + it_innergroup ;
}
//
// Outer Row Group Totals
//
if ( m_config_f . isShowingColumnTotals ( ) )
{
result + = TQString ( " %1 %2 " ) . arg ( i18n ( " Total " ) ) . arg ( it_outergroup . key ( ) ) ;
unsigned column = 1 ;
while ( column < m_numColumns ) {
for ( unsigned i = 0 ; i < m_rowTypeList . size ( ) ; + + i )
result + = TQString ( " , \" %1 \" " ) . arg ( ( * it_outergroup ) . m_total [ m_rowTypeList [ i ] ] [ column ] . formatMoney ( fraction , false ) ) ;
column + + ;
}
if ( m_config_f . isShowingRowTotals ( ) ) {
for ( unsigned i = 0 ; i < m_rowTypeList . size ( ) ; + + i )
result + = TQString ( " , \" %1 \" " ) . arg ( ( * it_outergroup ) . m_total [ m_rowTypeList [ i ] ] . m_total . formatMoney ( fraction , false ) ) ;
}
result + = " \n " ;
}
+ + it_outergroup ;
}
//
// Report Totals
//
if ( m_config_f . isShowingColumnTotals ( ) )
{
result + = i18n ( " Grand Total " ) ;
unsigned totalcolumn = 1 ;
while ( totalcolumn < m_numColumns ) {
for ( unsigned i = 0 ; i < m_rowTypeList . size ( ) ; + + i )
result + = TQString ( " , \" %1 \" " ) . arg ( m_grid . m_total [ m_rowTypeList [ i ] ] [ totalcolumn ] . formatMoney ( fraction , false ) ) ;
totalcolumn + + ;
}
if ( m_config_f . isShowingRowTotals ( ) ) {
for ( unsigned i = 0 ; i < m_rowTypeList . size ( ) ; + + i )
result + = TQString ( " , \" %1 \" " ) . arg ( m_grid . m_total [ m_rowTypeList [ i ] ] . m_total . formatMoney ( fraction , false ) ) ;
}
result + = " \n " ;
}
return result ;
}
TQString PivotTable : : renderHTML ( void ) const
{
DEBUG_ENTER ( __PRETTY_FUNCTION__ ) ;
TQString colspan = TQString ( " colspan= \" %1 \" " ) . arg ( m_numColumns + 1 + ( m_config_f . isShowingRowTotals ( ) ? 1 : 0 ) ) ;
//
// Report Title
//
TQString result = TQString ( " <h2 class= \" report \" >%1</h2> \n " ) . arg ( m_config_f . name ( ) ) ;
//actual dates of the report
result + = TQString ( " <div class= \" subtitle \" > " ) ;
result + = i18n ( " Report date range " , " %1 through %2 " ) . arg ( TDEGlobal : : locale ( ) - > formatDate ( m_config_f . fromDate ( ) , true ) ) . arg ( TDEGlobal : : locale ( ) - > formatDate ( m_config_f . toDate ( ) , true ) ) ;
result + = TQString ( " </div> \n " ) ;
result + = TQString ( " <div class= \" gap \" > </div> \n " ) ;
//currency conversion message
result + = TQString ( " <div class= \" subtitle \" > " ) ;
if ( m_config_f . isConvertCurrency ( ) )
result + = i18n ( " All currencies converted to %1 " ) . arg ( MyMoneyFile : : instance ( ) - > baseCurrency ( ) . name ( ) ) ;
else
result + = i18n ( " All values shown in %1 unless otherwise noted " ) . arg ( MyMoneyFile : : instance ( ) - > baseCurrency ( ) . name ( ) ) ;
result + = TQString ( " </div> \n " ) ;
result + = TQString ( " <div class= \" gap \" > </div> \n " ) ;
// setup a leftborder for better readability of budget vs actual reports
TQString leftborder ;
if ( m_rowTypeList . size ( ) > 1 )
leftborder = " class= \" leftborder \" " ;
//
// Table Header
//
result + = TQString ( " \n \n <table class= \" report \" cellspacing= \" 0 \" > \n "
" <thead><tr class= \" itemheader \" > \n <th>%1</th> " ) . arg ( i18n ( " Account " ) ) ;
TQString headerspan ;
int span = m_rowTypeList . size ( ) ;
headerspan = TQString ( " colspan= \" %1 \" " ) . arg ( span ) ;
unsigned column = 1 ;
while ( column < m_numColumns )
result + = TQString ( " <th%1>%2</th> " ) . arg ( headerspan , TQString ( m_columnHeadings [ column + + ] ) . replace ( TQRegExp ( " " ) , " <br> " ) ) ;
if ( m_config_f . isShowingRowTotals ( ) )
result + = TQString ( " <th%1>%2</th> " ) . arg ( headerspan ) . arg ( i18n ( " Total " ) ) ;
result + = " </tr></thead> \n " ;
//
// Header for multiple columns
//
if ( span > 1 )
{
result + = " <tr><td></td> " ;
unsigned column = 1 ;
while ( column < m_numColumns )
{
TQString lb ;
if ( column ! = 1 )
lb = leftborder ;
for ( unsigned i = 0 ; i < m_rowTypeList . size ( ) ; + + i ) {
result + = TQString ( " <td%2>%1</td> " )
. arg ( i18n ( m_columnTypeHeaderList [ i ] . utf8 ( ) ) )
. arg ( i = = 0 ? lb : TQString ( ) ) ;
}
column + + ;
}
if ( m_config_f . isShowingRowTotals ( ) ) {
for ( unsigned i = 0 ; i < m_rowTypeList . size ( ) ; + + i ) {
result + = TQString ( " <td%2>%1</td> " )
. arg ( i18n ( m_columnTypeHeaderList [ i ] . utf8 ( ) ) )
. arg ( i = = 0 ? leftborder : TQString ( ) ) ;
}
}
result + = " </tr> " ;
}
// Skip the body of the report if the report only calls for totals to be shown
if ( m_config_f . detailLevel ( ) ! = MyMoneyReport : : eDetailTotal )
{
//
// Outer groups
//
// Need to sort the outergroups. They can't always be sorted by name. So we create a list of
// map iterators, and sort that. Then we'll iterate through the map iterators and use those as
// before.
//
// I hope this doesn't bog the performance of reports, given that we're copying the entire report
// data. If this is a perf hit, we could change to storing outergroup pointers, I think.
TQValueList < PivotOuterGroup > outergroups ;
PivotGrid : : const_iterator it_outergroup_map = m_grid . begin ( ) ;
while ( it_outergroup_map ! = m_grid . end ( ) )
{
outergroups . push_back ( it_outergroup_map . data ( ) ) ;
// copy the name into the outergroup, because we will now lose any association with
// the map iterator
outergroups . back ( ) . m_displayName = it_outergroup_map . key ( ) ;
+ + it_outergroup_map ;
}
qHeapSort ( outergroups ) ;
TQValueList < PivotOuterGroup > : : const_iterator it_outergroup = outergroups . begin ( ) ;
while ( it_outergroup ! = outergroups . end ( ) )
{
//
// Outer Group Header
//
result + = TQString ( " <tr class= \" sectionheader \" ><td class= \" left \" %1>%2</td></tr> \n " ) . arg ( colspan ) . arg ( ( * it_outergroup ) . m_displayName ) ;
// Skip the inner groups if the report only calls for outer group totals to be shown
if ( m_config_f . detailLevel ( ) ! = MyMoneyReport : : eDetailGroup )
{
//
// Inner Groups
//
PivotOuterGroup : : const_iterator it_innergroup = ( * it_outergroup ) . begin ( ) ;
unsigned rownum = 0 ;
while ( it_innergroup ! = ( * it_outergroup ) . end ( ) )
{
//
// Rows
//
TQString innergroupdata ;
PivotInnerGroup : : const_iterator it_row = ( * it_innergroup ) . begin ( ) ;
while ( it_row ! = ( * it_innergroup ) . end ( ) )
{
//
// Columns
//
TQString rowdata ;
unsigned column = 1 ;
bool isUsed = it_row . data ( ) [ eActual ] [ 0 ] . isUsed ( ) ;
while ( column < m_numColumns )
{
TQString lb ;
if ( column ! = 1 )
lb = leftborder ;
for ( unsigned i = 0 ; i < m_rowTypeList . size ( ) ; + + i ) {
rowdata + = TQString ( " <td%2>%1</td> " )
. arg ( coloredAmount ( it_row . data ( ) [ m_rowTypeList [ i ] ] [ column ] ) )
. arg ( i = = 0 ? lb : TQString ( ) ) ;
isUsed | = it_row . data ( ) [ m_rowTypeList [ i ] ] [ column ] . isUsed ( ) ;
}
column + + ;
}
if ( m_config_f . isShowingRowTotals ( ) )
{
for ( unsigned i = 0 ; i < m_rowTypeList . size ( ) ; + + i ) {
rowdata + = TQString ( " <td%2>%1</td> " )
. arg ( coloredAmount ( it_row . data ( ) [ m_rowTypeList [ i ] ] . m_total ) )
. arg ( i = = 0 ? leftborder : TQString ( ) ) ;
}
}
//
// Row Header
//
ReportAccount rowname = it_row . key ( ) ;
// don't show closed accounts if they have not been used
if ( ! rowname . isClosed ( ) | | isUsed ) {
innergroupdata + = TQString ( " <tr class= \" row-%1 \" %2><td%3 class= \" left \" style= \" text-indent: %4.0em \" >%5%6</td> " )
. arg ( rownum & 0x01 ? " even " : " odd " )
. arg ( rowname . isTopLevel ( ) ? " id= \" topparent \" " : " " )
. arg ( " " ) //.arg((*it_row).m_total.isZero() ? colspan : "") // colspan the distance if this row will be blank
. arg ( rowname . hierarchyDepth ( ) - 1 )
. arg ( rowname . name ( ) . replace ( TQRegExp ( " " ) , " " ) )
. arg ( ( m_config_f . isConvertCurrency ( ) | | ! rowname . isForeignCurrency ( ) ) ? TQString ( ) : TQString ( " (%1) " ) . arg ( rowname . currency ( ) . id ( ) ) ) ;
// Don't print this row if it's going to be all zeros
// TODO: Uncomment this, and deal with the case where the data
// is zero, but the budget is non-zero
//if ( !(*it_row).m_total.isZero() )
innergroupdata + = rowdata ;
innergroupdata + = " </tr> \n " ;
}
+ + it_row ;
}
//
// Inner Row Group Totals
//
bool finishrow = true ;
TQString finalRow ;
bool isUsed = false ;
if ( m_config_f . detailLevel ( ) = = MyMoneyReport : : eDetailAll & & ( ( * it_innergroup ) . size ( ) > 1 ) )
{
// Print the individual rows
result + = innergroupdata ;
if ( m_config_f . isShowingColumnTotals ( ) )
{
// Start the TOTALS row
finalRow = TQString ( " <tr class= \" row-%1 \" id= \" subtotal \" ><td class= \" left \" > %2</td> " )
. arg ( rownum & 0x01 ? " even " : " odd " )
. arg ( i18n ( " Total " ) ) ;
// don't suppress display of totals
isUsed = true ;
}
else {
finishrow = false ;
+ + rownum ;
}
}
else
{
// Start the single INDIVIDUAL ACCOUNT row
// FIXME: There is a bit of a bug here with class=leftX. There's only a finite number
// of classes I can define in the .CSS file, and the user can theoretically nest deeper.
// The right solution is to use style=Xem, and calculate X. Let's see if anyone complains
// first :) Also applies to the row header case above.
// FIXED: I found it in one of my reports and changed it to the proposed method.
// This works for me (ipwizard)
ReportAccount rowname = ( * it_innergroup ) . begin ( ) . key ( ) ;
isUsed | = ! rowname . isClosed ( ) ;
finalRow = TQString ( " <tr class= \" row-%1 \" %2><td class= \" left \" style= \" text-indent: %3.0em; \" >%5%6</td> " )
. arg ( rownum & 0x01 ? " even " : " odd " )
. arg ( m_config_f . detailLevel ( ) = = MyMoneyReport : : eDetailAll ? " id= \" solo \" " : " " )
. arg ( rowname . hierarchyDepth ( ) - 1 )
. arg ( rowname . name ( ) . replace ( TQRegExp ( " " ) , " " ) )
. arg ( ( m_config_f . isConvertCurrency ( ) | | ! rowname . isForeignCurrency ( ) ) ? TQString ( ) : TQString ( " (%1) " ) . arg ( rowname . currency ( ) . id ( ) ) ) ;
}
// Finish the row started above, unless told not to
if ( finishrow )
{
unsigned column = 1 ;
isUsed | = ( * it_innergroup ) . m_total [ eActual ] [ 0 ] . isUsed ( ) ;
while ( column < m_numColumns )
{
TQString lb ;
if ( column ! = 1 )
lb = leftborder ;
for ( unsigned i = 0 ; i < m_rowTypeList . size ( ) ; + + i ) {
finalRow + = TQString ( " <td%2>%1</td> " )
. arg ( coloredAmount ( ( * it_innergroup ) . m_total [ m_rowTypeList [ i ] ] [ column ] ) )
. arg ( i = = 0 ? lb : TQString ( ) ) ;
isUsed | = ( * it_innergroup ) . m_total [ m_rowTypeList [ i ] ] [ column ] . isUsed ( ) ;
}
column + + ;
}
if ( m_config_f . isShowingRowTotals ( ) )
{
for ( unsigned i = 0 ; i < m_rowTypeList . size ( ) ; + + i ) {
finalRow + = TQString ( " <td%2>%1</td> " )
. arg ( coloredAmount ( ( * it_innergroup ) . m_total [ m_rowTypeList [ i ] ] . m_total ) )
. arg ( i = = 0 ? leftborder : TQString ( ) ) ;
}
}
finalRow + = " </tr> \n " ;
if ( isUsed ) {
result + = finalRow ;
+ + rownum ;
}
}
+ + it_innergroup ;
} // end while iterating on the inner groups
} // end if detail level is not "group"
//
// Outer Row Group Totals
//
if ( m_config_f . isShowingColumnTotals ( ) )
{
result + = TQString ( " <tr class= \" sectionfooter \" ><td class= \" left \" >%1 %2</td> " ) . arg ( i18n ( " Total " ) ) . arg ( ( * it_outergroup ) . m_displayName ) ;
unsigned column = 1 ;
while ( column < m_numColumns )
{
TQString lb ;
if ( column ! = 1 )
lb = leftborder ;
for ( unsigned i = 0 ; i < m_rowTypeList . size ( ) ; + + i ) {
result + = TQString ( " <td%2>%1</td> " )
. arg ( coloredAmount ( ( * it_outergroup ) . m_total [ m_rowTypeList [ i ] ] [ column ] ) )
. arg ( i = = 0 ? lb : TQString ( ) ) ;
}
column + + ;
}
if ( m_config_f . isShowingRowTotals ( ) )
{
for ( unsigned i = 0 ; i < m_rowTypeList . size ( ) ; + + i ) {
result + = TQString ( " <td%2>%1</td> " )
. arg ( coloredAmount ( ( * it_outergroup ) . m_total [ m_rowTypeList [ i ] ] . m_total ) )
. arg ( i = = 0 ? leftborder : TQString ( ) ) ;
}
}
result + = " </tr> \n " ;
}
+ + it_outergroup ;
} // end while iterating on the outergroups
} // end if detail level is not "total"
//
// Report Totals
//
if ( m_config_f . isShowingColumnTotals ( ) )
{
result + = TQString ( " <tr class= \" spacer \" ><td> </td></tr> \n " ) ;
result + = TQString ( " <tr class= \" reportfooter \" ><td class= \" left \" >%1</td> " ) . arg ( i18n ( " Grand Total " ) ) ;
unsigned totalcolumn = 1 ;
while ( totalcolumn < m_numColumns )
{
TQString lb ;
if ( totalcolumn ! = 1 )
lb = leftborder ;
for ( unsigned i = 0 ; i < m_rowTypeList . size ( ) ; + + i ) {
result + = TQString ( " <td%2>%1</td> " )
. arg ( coloredAmount ( m_grid . m_total [ m_rowTypeList [ i ] ] [ totalcolumn ] ) )
. arg ( i = = 0 ? lb : TQString ( ) ) ;
}
totalcolumn + + ;
}
if ( m_config_f . isShowingRowTotals ( ) )
{
for ( unsigned i = 0 ; i < m_rowTypeList . size ( ) ; + + i ) {
result + = TQString ( " <td%2>%1</td> " )
. arg ( coloredAmount ( m_grid . m_total [ m_rowTypeList [ i ] ] . m_total ) )
. arg ( i = = 0 ? leftborder : TQString ( ) ) ;
}
}
result + = " </tr> \n " ;
}
result + = TQString ( " <tr class= \" spacer \" ><td> </td></tr> \n " ) ;
result + = TQString ( " <tr class= \" spacer \" ><td> </td></tr> \n " ) ;
result + = " </table> \n " ;
return result ;
}
void PivotTable : : dump ( const TQString & file , const TQString & /* context */ ) const
{
TQFile g ( file ) ;
g . open ( IO_WriteOnly ) ;
TQTextStream ( & g ) < < renderHTML ( ) ;
g . close ( ) ;
}
# ifdef HAVE_KDCHART
void PivotTable : : drawChart ( KReportChartView & _view ) const
{
# if 1 // make this "#if 1" if you want to play with the axis settings
// not sure if 0 is X and 1 is Y.
KDChartAxisParams xAxisParams , yAxisParams ;
KDChartAxisParams : : deepCopy ( xAxisParams , _view . params ( ) - > axisParams ( 0 ) ) ;
KDChartAxisParams : : deepCopy ( yAxisParams , _view . params ( ) - > axisParams ( 1 ) ) ;
// modify axis settings here
xAxisParams . setAxisLabelsFontMinSize ( 12 ) ;
xAxisParams . setAxisLabelsFontRelSize ( 20 ) ;
yAxisParams . setAxisLabelsFontMinSize ( 12 ) ;
yAxisParams . setAxisLabelsFontRelSize ( 20 ) ;
_view . params ( ) - > setAxisParams ( 0 , xAxisParams ) ;
_view . params ( ) - > setAxisParams ( 1 , yAxisParams ) ;
# endif
_view . params ( ) - > setLegendFontRelSize ( 20 ) ;
_view . params ( ) - > setLegendTitleFontRelSize ( 24 ) ;
_view . params ( ) - > setLegendTitleText ( i18n ( " Legend " ) ) ;
_view . params ( ) - > setAxisShowGrid ( 0 , m_config_f . isChartGridLines ( ) ) ;
_view . params ( ) - > setAxisShowGrid ( 1 , m_config_f . isChartGridLines ( ) ) ;
_view . params ( ) - > setPrintDataValues ( m_config_f . isChartDataLabels ( ) ) ;
// whether to limit the chart to use series totals only. Used for reports which only
// show one dimension (pie).
bool seriesTotals = false ;
// whether series (rows) are accounts (true) or months (false). This causes a lot
// of complexity in the charts. The problem is that circular reports work best with
// an account in a COLUMN, while line/bar prefer it in a ROW.
bool accountSeries = true ;
//what values should be shown
bool showBudget = m_config_f . hasBudget ( ) ;
bool showForecast = m_config_f . isIncludingForecast ( ) ;
bool showActual = false ;
if ( ( m_config_f . isIncludingBudgetActuals ( ) ) | | ( ! showBudget & & ! showForecast ) )
showActual = true ;
_view . params ( ) - > setLineWidth ( m_config_f . chartLineWidth ( ) ) ;
switch ( m_config_f . chartType ( ) )
{
case MyMoneyReport : : eChartNone :
case MyMoneyReport : : eChartEnd :
case MyMoneyReport : : eChartLine :
_view . params ( ) - > setChartType ( KDChartParams : : Line ) ;
_view . params ( ) - > setAxisDatasets ( 0 , 0 ) ;
break ;
case MyMoneyReport : : eChartBar :
_view . params ( ) - > setChartType ( KDChartParams : : Bar ) ;
_view . params ( ) - > setBarChartSubType ( KDChartParams : : BarNormal ) ;
break ;
case MyMoneyReport : : eChartStackedBar :
_view . params ( ) - > setChartType ( KDChartParams : : Bar ) ;
_view . params ( ) - > setBarChartSubType ( KDChartParams : : BarStacked ) ;
break ;
case MyMoneyReport : : eChartPie :
_view . params ( ) - > setChartType ( KDChartParams : : Pie ) ;
// Charts should only be 3D if this adds any information
_view . params ( ) - > setThreeDPies ( false ) ;
accountSeries = false ;
seriesTotals = true ;
break ;
case MyMoneyReport : : eChartRing :
_view . params ( ) - > setChartType ( KDChartParams : : Ring ) ;
_view . params ( ) - > setRelativeRingThickness ( true ) ;
accountSeries = false ;
break ;
}
// For onMouseOver events, we want to activate mouse tracking
_view . setMouseTracking ( true ) ;
//
// In KDChart parlance, a 'series' (or row) is an account (or accountgroup, etc)
// and an 'item' (or column) is a month
//
unsigned r ;
unsigned c ;
if ( accountSeries )
{
r = 1 ;
c = m_numColumns - 1 ;
}
else
{
c = 1 ;
r = m_numColumns - 1 ;
}
KDChartTableData data ( r , c ) ;
// The KReportChartView widget needs to know whether the legend
// corresponds to rows or columns
_view . setAccountSeries ( accountSeries ) ;
// Set up X axis labels (ie "abscissa" to use the technical term)
TQStringList & abscissaNames = _view . abscissaNames ( ) ;
abscissaNames . clear ( ) ;
if ( accountSeries )
{
unsigned column = 1 ;
while ( column < m_numColumns ) {
abscissaNames + = TQString ( m_columnHeadings [ column + + ] ) . replace ( " " , " " ) ;
}
}
else
{
// we will set these up while putting in the chart values.
}
switch ( m_config_f . detailLevel ( ) )
{
case MyMoneyReport : : eDetailNone :
case MyMoneyReport : : eDetailEnd :
case MyMoneyReport : : eDetailAll :
{
unsigned rowNum = 0 ;
// iterate over outer groups
PivotGrid : : const_iterator it_outergroup = m_grid . begin ( ) ;
while ( it_outergroup ! = m_grid . end ( ) )
{
// iterate over inner groups
PivotOuterGroup : : const_iterator it_innergroup = ( * it_outergroup ) . begin ( ) ;
while ( it_innergroup ! = ( * it_outergroup ) . end ( ) )
{
//
// Rows
//
TQString innergroupdata ;
PivotInnerGroup : : const_iterator it_row = ( * it_innergroup ) . begin ( ) ;
while ( it_row ! = ( * it_innergroup ) . end ( ) )
{
//Do not include investments accounts in the chart because they are merely container of stock and other accounts
if ( it_row . key ( ) . accountType ( ) ! = MyMoneyAccount : : Investment ) {
//iterate row types
for ( unsigned i = 0 ; i < m_rowTypeList . size ( ) ; + + i ) {
//skip the budget difference rowset
if ( m_rowTypeList [ i ] ! = eBudgetDiff ) {
rowNum = drawChartRowSet ( rowNum , seriesTotals , accountSeries , data , it_row . data ( ) , m_rowTypeList [ i ] ) ;
//only show the column type in the header if there is more than one type
if ( m_rowTypeList . size ( ) > 1 ) {
_view . params ( ) - > setLegendText ( rowNum - 1 , m_columnTypeHeaderList [ i ] + " - " + it_row . key ( ) . name ( ) ) ;
} else {
_view . params ( ) - > setLegendText ( rowNum - 1 , it_row . key ( ) . name ( ) ) ;
}
}
}
}
+ + it_row ;
}
+ + it_innergroup ;
}
+ + it_outergroup ;
}
}
break ;
case MyMoneyReport : : eDetailTop :
{
unsigned rowNum = 0 ;
// iterate over outer groups
PivotGrid : : const_iterator it_outergroup = m_grid . begin ( ) ;
while ( it_outergroup ! = m_grid . end ( ) )
{
// iterate over inner groups
PivotOuterGroup : : const_iterator it_innergroup = ( * it_outergroup ) . begin ( ) ;
while ( it_innergroup ! = ( * it_outergroup ) . end ( ) )
{
//iterate row types
for ( unsigned i = 0 ; i < m_rowTypeList . size ( ) ; + + i ) {
//skip the budget difference rowset
if ( m_rowTypeList [ i ] ! = eBudgetDiff ) {
rowNum = drawChartRowSet ( rowNum , seriesTotals , accountSeries , data , ( * it_innergroup ) . m_total , m_rowTypeList [ i ] ) ;
//only show the column type in the header if there is more than one type
if ( m_rowTypeList . size ( ) > 1 ) {
_view . params ( ) - > setLegendText ( rowNum - 1 , m_columnTypeHeaderList [ i ] + " - " + it_innergroup . key ( ) ) ;
} else {
_view . params ( ) - > setLegendText ( rowNum - 1 , it_innergroup . key ( ) ) ;
}
}
}
+ + it_innergroup ;
}
+ + it_outergroup ;
}
}
break ;
case MyMoneyReport : : eDetailGroup :
{
unsigned rowNum = 0 ;
// iterate over outer groups
PivotGrid : : const_iterator it_outergroup = m_grid . begin ( ) ;
while ( it_outergroup ! = m_grid . end ( ) )
{
//iterate row types
for ( unsigned i = 0 ; i < m_rowTypeList . size ( ) ; + + i ) {
//skip the budget difference rowset
if ( m_rowTypeList [ i ] ! = eBudgetDiff ) {
rowNum = drawChartRowSet ( rowNum , seriesTotals , accountSeries , data , ( * it_outergroup ) . m_total , m_rowTypeList [ i ] ) ;
//only show the column type in the header if there is more than one type
if ( m_rowTypeList . size ( ) > 1 ) {
_view . params ( ) - > setLegendText ( rowNum - 1 , m_columnTypeHeaderList [ i ] + " - " + it_outergroup . key ( ) ) ;
} else {
_view . params ( ) - > setLegendText ( rowNum - 1 , it_outergroup . key ( ) ) ;
}
}
}
+ + it_outergroup ;
}
//if selected, show totals too
if ( m_config_f . isShowingRowTotals ( ) )
{
//iterate row types
for ( unsigned i = 0 ; i < m_rowTypeList . size ( ) ; + + i ) {
//skip the budget difference rowset
if ( m_rowTypeList [ i ] ! = eBudgetDiff ) {
rowNum = drawChartRowSet ( rowNum , seriesTotals , accountSeries , data , m_grid . m_total , m_rowTypeList [ i ] ) ;
//only show the column type in the header if there is more than one type
if ( m_rowTypeList . size ( ) > 1 ) {
_view . params ( ) - > setLegendText ( rowNum - 1 , m_columnTypeHeaderList [ i ] + " - " + i18n ( " Total " ) ) ;
} else {
_view . params ( ) - > setLegendText ( rowNum - 1 , i18n ( " Total " ) ) ;
}
}
}
}
}
break ;
case MyMoneyReport : : eDetailTotal :
{
unsigned rowNum = 0 ;
//iterate row types
for ( unsigned i = 0 ; i < m_rowTypeList . size ( ) ; + + i ) {
//skip the budget difference rowset
if ( m_rowTypeList [ i ] ! = eBudgetDiff ) {
rowNum = drawChartRowSet ( rowNum , seriesTotals , accountSeries , data , m_grid . m_total , m_rowTypeList [ i ] ) ;
//only show the column type in the header if there is more than one type
if ( m_rowTypeList . size ( ) > 1 ) {
_view . params ( ) - > setLegendText ( rowNum - 1 , m_columnTypeHeaderList [ i ] + " - " + i18n ( " Total " ) ) ;
} else {
_view . params ( ) - > setLegendText ( rowNum - 1 , i18n ( " Total " ) ) ;
}
}
}
}
break ;
}
_view . setNewData ( data ) ;
// make sure to show only the required number of fractional digits on the labels of the graph
_view . params ( ) - > setDataValuesCalc ( 0 , MyMoneyMoney : : denomToPrec ( MyMoneyFile : : instance ( ) - > baseCurrency ( ) . smallestAccountFraction ( ) ) ) ;
_view . refreshLabels ( ) ;
#if 0
// I have not been able to get this to work (ace)
//
// Set line to dashed for the future
//
if ( accountSeries )
{
// the first column of report which represents a date in the future, or one past the
// last column if all columns are in the present day. Only relevant when accountSeries==true
unsigned futurecolumn = columnValue ( TQDate : : currentDate ( ) ) - columnValue ( m_beginDate ) + 1 ;
// kdDebug(2) << "futurecolumn: " << futurecolumn << endl;
// kdDebug(2) << "m_numColumns: " << m_numColumns << endl;
// Properties for line charts whose values are in the future.
KDChartPropertySet propSetFutureValue ( " future value " , KDChartParams : : KDCHART_PROPSET_NORMAL_DATA ) ;
propSetFutureValue . setLineStyle ( KDChartPropertySet : : OwnID , TQt : : DotLine ) ;
const int idPropFutureValue = _view . params ( ) - > registerProperties ( propSetFutureValue ) ;
for ( int col = futurecolumn ; col < m_numColumns ; + + col ) {
_view . setProperty ( 0 , col , idPropFutureValue ) ;
}
}
# endif
}
# else
void PivotTable : : drawChart ( KReportChartView & ) const { }
# endif
unsigned PivotTable : : drawChartRowSet ( unsigned rowNum , const bool seriesTotals , const bool accountSeries , KDChartTableData & data , const PivotGridRowSet & rowSet , const ERowType rowType ) const
{
//only add a row if one has been added before
// TODO: This is inefficient. Really we should total up how many rows
// there will be and allocate it all at once.
if ( rowNum > 0 ) {
if ( accountSeries )
data . expand ( rowNum + 1 , m_numColumns - 1 ) ;
else
data . expand ( m_numColumns - 1 , rowNum + 1 ) ;
}
// Columns
if ( seriesTotals )
{
if ( accountSeries )
data . setCell ( rowNum , 0 , rowSet [ rowType ] . m_total . toDouble ( ) ) ;
else
data . setCell ( 0 , rowNum , rowSet [ rowType ] . m_total . toDouble ( ) ) ;
}
else
{
unsigned column = 1 ;
while ( column < m_numColumns )
{
if ( accountSeries )
data . setCell ( rowNum , column - 1 , rowSet [ rowType ] [ column ] . toDouble ( ) ) ;
else
data . setCell ( column - 1 , rowNum , rowSet [ rowType ] [ column ] . toDouble ( ) ) ;
+ + column ;
}
}
return + + rowNum ;
}
TQString PivotTable : : coloredAmount ( const MyMoneyMoney & amount , const TQString & currencySymbol , int prec ) const
{
TQString result ;
if ( amount . isNegative ( ) )
result + = TQString ( " <font color= \" rgb(%1,%2,%3) \" > " )
. arg ( KMyMoneyGlobalSettings : : listNegativeValueColor ( ) . red ( ) )
. arg ( KMyMoneyGlobalSettings : : listNegativeValueColor ( ) . green ( ) )
. arg ( KMyMoneyGlobalSettings : : listNegativeValueColor ( ) . blue ( ) ) ;
result + = amount . formatMoney ( currencySymbol , prec ) ;
if ( amount . isNegative ( ) )
result + = TQString ( " </font> " ) ;
return result ;
}
void PivotTable : : calculateBudgetDiff ( void )
{
PivotGrid : : iterator it_outergroup = m_grid . begin ( ) ;
while ( it_outergroup ! = m_grid . end ( ) )
{
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 ( ) )
{
unsigned column = 1 ;
switch ( it_row . key ( ) . accountGroup ( ) )
{
case MyMoneyAccount : : Income :
case MyMoneyAccount : : Asset :
while ( column < m_numColumns ) {
it_row . data ( ) [ eBudgetDiff ] [ column ] = it_row . data ( ) [ eActual ] [ column ] - it_row . data ( ) [ eBudget ] [ column ] ;
+ + column ;
}
break ;
case MyMoneyAccount : : Expense :
case MyMoneyAccount : : Liability :
while ( column < m_numColumns ) {
it_row . data ( ) [ eBudgetDiff ] [ column ] = it_row . data ( ) [ eBudget ] [ column ] - it_row . data ( ) [ eActual ] [ column ] ;
+ + column ;
}
break ;
default :
break ;
}
+ + it_row ;
}
+ + it_innergroup ;
}
+ + it_outergroup ;
}
}
void PivotTable : : calculateForecast ( void )
{
//setup forecast
MyMoneyForecast forecast ;
//setup forecast settings
//since this is a net worth forecast we want to include all account even those that are not in use
forecast . setIncludeUnusedAccounts ( true ) ;
//setup forecast dates
if ( m_endDate > TQDate : : currentDate ( ) ) {
forecast . setForecastEndDate ( m_endDate ) ;
forecast . setForecastStartDate ( TQDate : : currentDate ( ) ) ;
forecast . setForecastDays ( TQDate : : currentDate ( ) . daysTo ( m_endDate ) ) ;
} else {
forecast . setForecastStartDate ( m_beginDate ) ;
forecast . setForecastEndDate ( m_endDate ) ;
forecast . setForecastDays ( m_beginDate . daysTo ( m_endDate ) + 1 ) ;
}
//adjust history dates if beginning date is before today
if ( m_beginDate < TQDate : : currentDate ( ) ) {
forecast . setHistoryEndDate ( m_beginDate . addDays ( - 1 ) ) ;
forecast . setHistoryStartDate ( forecast . historyEndDate ( ) . addDays ( - forecast . accountsCycle ( ) * forecast . forecastCycles ( ) ) ) ;
}
//run forecast
forecast . doForecast ( ) ;
//go through the data and add forecast
PivotGrid : : iterator it_outergroup = m_grid . begin ( ) ;
while ( it_outergroup ! = m_grid . end ( ) )
{
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 ( ) )
{
unsigned column = 1 ;
TQDate forecastDate = m_beginDate ;
//check whether columns are days or months
if ( m_config_f . isColumnsAreDays ( ) )
{
while ( column < m_numColumns ) {
it_row . data ( ) [ eForecast ] [ column ] = forecast . forecastBalance ( it_row . key ( ) , forecastDate ) ;
forecastDate = forecastDate . addDays ( 1 ) ;
+ + column ;
}
} else {
//if columns are months
while ( column < m_numColumns ) {
//set forecastDate to last day of each month
//TODO we really need a date manipulation util
forecastDate = TQDate ( forecastDate . year ( ) , forecastDate . month ( ) , forecastDate . daysInMonth ( ) ) ;
//check that forecastDate is not over ending date
if ( forecastDate > m_endDate )
forecastDate = m_endDate ;
//get forecast balance and set the corresponding column
it_row . data ( ) [ eForecast ] [ column ] = forecast . forecastBalance ( it_row . key ( ) , forecastDate ) ;
forecastDate = forecastDate . addDays ( 1 ) ;
+ + column ;
}
}
+ + it_row ;
}
+ + it_innergroup ;
}
+ + it_outergroup ;
}
}
void PivotTable : : loadRowTypeList ( )
{
if ( ( m_config_f . isIncludingBudgetActuals ( ) ) | |
( ! m_config_f . hasBudget ( )
& & ! m_config_f . isIncludingForecast ( )
& & ! m_config_f . isIncludingMovingAverage ( )
& & ! m_config_f . isIncludingPrice ( )
& & ! m_config_f . isIncludingAveragePrice ( ) )
) {
m_rowTypeList . append ( eActual ) ;
m_columnTypeHeaderList . append ( i18n ( " Actual " ) ) ;
}
if ( m_config_f . hasBudget ( ) ) {
m_rowTypeList . append ( eBudget ) ;
m_columnTypeHeaderList . append ( i18n ( " Budget " ) ) ;
}
if ( m_config_f . isIncludingBudgetActuals ( ) ) {
m_rowTypeList . append ( eBudgetDiff ) ;
m_columnTypeHeaderList . append ( i18n ( " Difference " ) ) ;
}
if ( m_config_f . isIncludingForecast ( ) ) {
m_rowTypeList . append ( eForecast ) ;
m_columnTypeHeaderList . append ( i18n ( " Forecast " ) ) ;
}
if ( m_config_f . isIncludingMovingAverage ( ) ) {
m_rowTypeList . append ( eAverage ) ;
m_columnTypeHeaderList . append ( i18n ( " Moving Average " ) ) ;
}
if ( m_config_f . isIncludingAveragePrice ( ) ) {
m_rowTypeList . append ( eAverage ) ;
m_columnTypeHeaderList . append ( i18n ( " Moving Average Price " ) ) ;
}
if ( m_config_f . isIncludingPrice ( ) ) {
m_rowTypeList . append ( ePrice ) ;
m_columnTypeHeaderList . append ( i18n ( " Price " ) ) ;
}
}
void PivotTable : : calculateMovingAverage ( void )
{
int delta = m_config_f . movingAverageDays ( ) / 2 ;
//go through the data and add the moving average
PivotGrid : : iterator it_outergroup = m_grid . begin ( ) ;
while ( it_outergroup ! = m_grid . end ( ) )
{
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 ( ) )
{
unsigned column = 1 ;
//check whether columns are days or months
if ( m_config_f . columnType ( ) = = MyMoneyReport : : eDays ) {
while ( column < m_numColumns ) {
MyMoneyMoney totalPrice = MyMoneyMoney ( 0 , 1 ) ;
TQDate averageStart = columnDate ( column ) . addDays ( - delta ) ;
TQDate averageEnd = columnDate ( column ) . addDays ( delta ) ;
for ( TQDate averageDate = averageStart ; averageDate < = averageEnd ; averageDate = averageDate . addDays ( 1 ) ) {
if ( m_config_f . isConvertCurrency ( ) ) {
totalPrice + = it_row . key ( ) . deepCurrencyPrice ( averageDate ) * it_row . key ( ) . baseCurrencyPrice ( averageDate ) ;
} else {
totalPrice + = it_row . key ( ) . deepCurrencyPrice ( averageDate ) ;
}
totalPrice = totalPrice . convert ( 10000 ) ;
}
//calculate the average price
MyMoneyMoney averagePrice = totalPrice / MyMoneyMoney ( ( averageStart . daysTo ( averageEnd ) + 1 ) , 1 ) ;
//get the actual value, multiply by the average price and save that value
MyMoneyMoney averageValue = it_row . data ( ) [ eActual ] [ column ] * averagePrice ;
it_row . data ( ) [ eAverage ] [ column ] = averageValue . convert ( 10000 ) ;
+ + column ;
}
} else {
//if columns are months
while ( column < m_numColumns ) {
TQDate averageStart = columnDate ( column ) ;
//set the right start date depending on the column type
switch ( m_config_f . columnType ( ) ) {
case MyMoneyReport : : eYears :
{
averageStart = TQDate ( columnDate ( column ) . year ( ) , 1 , 1 ) ;
break ;
}
case MyMoneyReport : : eBiMonths :
{
averageStart = TQDate ( columnDate ( column ) . year ( ) , columnDate ( column ) . month ( ) , 1 ) . addMonths ( - 1 ) ;
break ;
}
case MyMoneyReport : : eQuarters :
{
averageStart = TQDate ( columnDate ( column ) . year ( ) , columnDate ( column ) . month ( ) , 1 ) . addMonths ( - 1 ) ;
break ;
}
case MyMoneyReport : : eMonths :
{
averageStart = TQDate ( columnDate ( column ) . year ( ) , columnDate ( column ) . month ( ) , 1 ) ;
break ;
}
case MyMoneyReport : : eWeeks :
{
averageStart = columnDate ( column ) . addDays ( - columnDate ( column ) . dayOfWeek ( ) + 1 ) ;
break ;
}
default :
break ;
}
//gather the actual data and calculate the average
MyMoneyMoney totalPrice = MyMoneyMoney ( 0 , 1 ) ;
TQDate averageEnd = columnDate ( column ) ;
for ( TQDate averageDate = averageStart ; averageDate < = averageEnd ; averageDate = averageDate . addDays ( 1 ) ) {
if ( m_config_f . isConvertCurrency ( ) ) {
totalPrice + = it_row . key ( ) . deepCurrencyPrice ( averageDate ) * it_row . key ( ) . baseCurrencyPrice ( averageDate ) ;
} else {
totalPrice + = it_row . key ( ) . deepCurrencyPrice ( averageDate ) ;
}
totalPrice = totalPrice . convert ( 10000 ) ;
}
MyMoneyMoney averagePrice = totalPrice / MyMoneyMoney ( ( averageStart . daysTo ( averageEnd ) + 1 ) , 1 ) ;
MyMoneyMoney averageValue = it_row . data ( ) [ eActual ] [ column ] * averagePrice ;
//fill in the average
it_row . data ( ) [ eAverage ] [ column ] = averageValue . convert ( 10000 ) ;
+ + column ;
}
}
+ + it_row ;
}
+ + it_innergroup ;
}
+ + it_outergroup ;
}
}
void PivotTable : : fillBasePriceUnit ( ERowType rowType )
{
//go through the data and add forecast
PivotGrid : : iterator it_outergroup = m_grid . begin ( ) ;
while ( it_outergroup ! = m_grid . end ( ) )
{
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 ( ) )
{
unsigned column = 1 ;
while ( column < m_numColumns ) {
//insert a unit of currency for each account
it_row . data ( ) [ rowType ] [ column ] = MyMoneyMoney ( 1 , 1 ) ;
+ + column ;
}
+ + it_row ;
}
+ + it_innergroup ;
}
+ + it_outergroup ;
}
}
void PivotTable : : 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_f . 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_f . addAccount ( * it_b ) ;
}
}
}
}
}
}
}
} // namespace