/************************* * * * * * * * * * * * * *************************** Copyright (c) 1999-2005 Ryan Bobko ryan@ostrich-emulators.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. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ************************** * * * * * * * * * * * * **************************/ #include "lreport.h" #include "qhacc.h" #include "qhaccutils.h" #include "qhacctable.h" #include "qhaccsegmenter.h" #include // plugin factory calls extern "C" { QHaccPlugin * create(){ return new ProfitLossReport; } void destroy( ProfitLossReport * p ){ delete p; } } ProfitLossReport::ProfitLossReport(){} ProfitLossReport::~ProfitLossReport(){} auto_ptr ProfitLossReport::generate( uint jid, QHaccResultSet * accts, const QDate& start, const QDate& end, QString& title ){ handleAccounts( accts ); title=titler( accts, jid, start, end ); return igen( accts, gentrans( accts, jid, start, end ), start, end ); } auto_ptr ProfitLossReport::generate( QHaccResultSet * accts, vector ss, QString& title ){ handleAccounts( accts ); title=titler( accts ); return igen( accts, gentrans( accts, ss ) ); } void ProfitLossReport::handleAccounts( QHaccResultSet * accts ) const { if( !engine->getBP( "ALLACCOUNTSINPROFITLOSS" ) ){ // in most cases, we only want to run a profit/loss report on asset // accounts, so if that's the case, figure out what those accounts are uint rows=0; vector ts( 1, TableSelect( QC::ATYPE, QC::ASSET ) ); auto_ptr assets=engine->getWhere( ACCOUNTS, ts, rows ); ( ( QHaccTable * )accts )->clear(); accts->load( assets.get() ); } } auto_ptr ProfitLossReport::igen( QHaccResultSet * accts, auto_ptr trs, QDate start, QDate end ){ const MonCon& conv=engine->converter(); if( trs->rows()>0 && !start.isValid() ){ QHaccTableIndex idx( trs.get(), QC::XTDATE, CTDATE ); start=idx.min().getd(); end=idx.max().getd(); } bool rollups=engine->getBP( "INCLUDESUBSONRECALC" ); int osum=0; uint arows=accts->rows(); for( uint i=0; iat( i ); osum+=engine->getABalOn( a, start, TableSelect() ); if( rollups ){ uint rr=0; vector vs( 1, TableSelect( QC::APID, a[QC::AID] ) ); auto_ptr cs=engine->getWhere( ACCOUNTS, vs, rr ); for( uint j=0; jgetABalOn( cs->at( j ), start, TableSelect() ); } } } // profit/loss by category. sort the trans table by split's account // and sum the resulting transactions. auto_ptrret( new QHaccResultSet( 2 ) ); QHaccTable tret( *ret ); bool payees=engine->getBP( "GRAPHPIEPAYEES" ); bool de=engine->getBP( "DOUBLEENTRY" ); // if we're in single-entry mode, or if we're sorting on payee, // don't worry about the split account stuff uint * pos=0, sz=0; QString temp; if( payees || !de ){ QHaccTableIndex idx( trs.get(), QC::XTPAYEE, CTSTRING ); QHaccSegmenter::segment( engine, trs.get(), &idx, pos, sz ); tret.startLoad( sz-1 ); for( uint i=0; iat( idx[pos[i]] ); int sum=0; for( uint j=pos[i]; jat( idx[j] )[QC::XSSUM].gets(), Engine, Engine ); TableCol cols[]={ TableCol( " "+t.gets( QC::XTPAYEE )+ "("+temp.setNum( pos[i+1]-pos[i] )+")" ), TableCol( conv.convert( sum ) ) }; tret+=TableRow( cols, 2 ); } tret.stopLoad(); } else{ // we need to categorize by split category, not payee, so we're only // really interested in trans' split pairs QHaccTable tempt( QC::XCOLS, QC::SCOLTYPES ); uint trows=trs->rows(); tempt.startLoad( trows ); for( uint i=0; iat( i ); const Transaction& tr=engine->splitXTrans( xt ); QHaccTable spl=engine->getTSplits( xt[QC::XTID].getu() ); spl.deleteWhere( TableSelect( QC::SACCTID, xt[QC::XSACCTID] ) ); for( uint j=0; jmakeXTrans( tr, s ); } } tempt.stopLoad(); // sort transactions based on accountid QHaccTableIndex idx( &tempt, QC::XSACCTID, CTUINT ); QHaccSegmenter::segment( engine, &tempt, &idx, pos, sz ); for( uint i=0; igetFNameOfA( t[QC::XSACCTID].getu() ); TableCol cols[]={ " "+aname.gets()+ "("+temp.setNum( pos[i+1]-pos[i] )+")", TableCol( conv.convert( sum ) ) }; tret+=TableRow( cols, 2 ); } } // at this point, all the rows we want to display in the tret table, // but we need to resort them so all credits are together and all // debits are, too. then, we can add them to the return table, ret QHaccTableIndex idx( &tret, 1, CTFLOAT, 0, CTSTRING ); uint trows=tret.rows(); ret->startLoad( trows+5 ); TableCol tc[]={ TableCol( "Opening Balance" ), TableCol( conv.convert( osum ) ) }; TableRow line( tc, 2 ); ret->add( line ); line.set( 0, TableCol( "Assets" ) ); line.set( 1, TableCol( "" ) ); ret->add( line ); int lastsum=0; int credits=0; int debits=0; for( uint i=0; i0 ) credits+=thissum; else debits+=thissum; if( lastsum>=0 && thissum<0 ){ // we've gone from credits to debits line.set( 0, TableCol( "Total Assets" ) ); line.set( 1, conv.convert( credits ) ); ret->add( line ); line.set( 0, TableCol( "Liabilities" ) ); line.set( 1, "" ); ret->add( line ); } lastsum=thissum; if( thissum!=0 ) ret->add( cur ); } line.set( 0, TableCol( "Total Liabilities" ) ); line.set( 1, TableCol( conv.convert( debits ) ) ); ret->add( line ); line.set( 0, TableCol( "Ending Difference" ) ); line.set( 1, TableCol( conv.convert( credits+debits ) ) ); ret->add( line ); line.set( 0, TableCol( "Ending Balance" ) ); line.set( 1, TableCol( conv.convert( osum+credits+debits ) ) ); ret->add( line ); ret->stopLoad(); return ret; } void ProfitLossReport::selected( QDate& start, QDate& stop, bool& enableacctsel, bool& enablemultiselect ){ if( engine->getBP( "ALLACCOUNTSINPROFITLOSS" ) ){ ReportBase::selected( start, stop, enableacctsel, enablemultiselect ); } else{ start=stop.addMonths( -1 ); enableacctsel=enablemultiselect=false; } } const ProfitLossInfo ProfitLossReport::pinfo; const PluginInfo& ProfitLossReport::info() const { return pinfo; }