	private void getConversionRates( CSpSelect origDataObject, Vector currencies ) throws CHpInvalidExchangeRateException, Exception
	{
		CSpHashTable ratesTable = new CSpHashTable();										// Stores the conversion rates
		String baseCurrency = CHpeFinanceUtil.HP_STR_CURRENCY_CONV_BASE_CURRENCY;	// Base currency for conversions
		
		// Check that there are not too many currencies
		if ( currencies.size() > CHpReportsPage.HP_REPORTS_MAX_CURRENCIES )
		{
			throw new Exception(	"Too many currencies passed to getConversionRates() - "
										+ "Max = " + CHpReportsPage.HP_REPORTS_MAX_CURRENCIES
										+ "; currencies = " + currencies );
		}
		
		// Establish whether there are any dynamic or filter criteria specified for the original search data object - 
		// if there are none, we don't want to add them to the query below, so use an empty string
		String selectJoin			= (	origDataObject.getSelectJoinStr().length() > 0 ?
												origDataObject.getSelectJoinStr() + " AND " : "" );
		String dynamicCritera	= (	origDataObject.getDynamicCriteria().length() > 0 ?
												origDataObject.getDynamicCriteria() + " AND " : "" );
		String filterCriteria	= (	origDataObject.getFilterStr().length() > 0 ?
												origDataObject.getFilterStr() + " AND " : "" );
		
		// Build the SQL query, starting with the tables and criteria used by the report's original search data object,
		// using the following methods of the original data object:
		// 	getTablesListStr():		returns all tables specified in the studio
		// 	getSelectJoinStr():		returns all joins specified in the studio
		// 	getFilterStr():			returns all 'WHERE' criteria specified in the studio
		// 	getDynamicCriteria():	returns all 'WHERE' criteria added in the code
		StringBuffer columns = new StringBuffer( "SELECT dbo.proposal.proposal_id, dbo.proposal.proposal_version" );
		StringBuffer tables = new StringBuffer( " FROM " + origDataObject.getTablesListStr() + ", dbo.currency_conv a" );
		StringBuffer criteria = new StringBuffer( " WHERE " + selectJoin + dynamicCritera + filterCriteria +
																" a.from_currency_code = '" + baseCurrency + "'" +
																" AND a.to_currency_code = dbo.proposal.currency_code" +
																" AND ISNULL( a.active_date, '1900/01/01' )" +
																" <= dbo.proposal.last_pricing_date" +
																" AND ISNULL( a.inactive_date, '2100/01/01' )" +
																" > dbo.proposal.last_pricing_date" );

		DEBUG_THIS_MSG( "getConversionRates: getTablesListStr = " + origDataObject.getTablesListStr() );
		DEBUG_THIS_MSG( "getConversionRates: getSelectJoinStr = " + origDataObject.getSelectJoinStr() );
		DEBUG_THIS_MSG( "getConversionRates: getDynamicCriteria = " + origDataObject.getDynamicCriteria() );
		DEBUG_THIS_MSG( "getConversionRates: getFilterStr = " + origDataObject.getFilterStr() );
		
		// Loop through the currencies to be displayed in the report, and add details of the columns, tables
		// and criteria needed to build the SQL statement to retrieve rates for all report currencies.
		for ( int i = 0; i < currencies.size(); i++ )
		{
			// Multiple joins to the currency_conv table are needed, so create a unique alias for this currency
			char tableAlias = (char) ( 'b' + i );
			
			// Populate the arrays
			columns.append( ", ISNULL(( " + tableAlias + ".conv_rate / a.conv_rate ), 0 )" );
			tables.append( ", currency_conv " + tableAlias );
			criteria.append(	" AND " + tableAlias + ".from_currency_code = '" + baseCurrency + "'" +
									" AND " + tableAlias + ".to_currency_code = '" + currencies.elementAt( i ) + "'" +
									" AND ISNULL( " + tableAlias + ".active_date, '1900/01/01' ) <= proposal.last_pricing_date" +
									" AND ISNULL( " + tableAlias + ".inactive_date, '2100/01/01' ) > proposal.last_pricing_date" );
		}
		
		StringBuffer query = columns.append( tables ).append( criteria );
		
		DEBUG_THIS_MSG( "getConversionRates : SQL query = " + query );
	
		// Execute the query
		CSpDBResult result = CSpDataObject.executeImmediate ( "ds_efinancedb", query.toString() );
		
		// Get the status object from the query
		CSpDBResultStatus status = result.getResultStatus();
		
		if ( status.getErrorCode() != CSpDataObject.SUCCESS && status.getErrorCode() != CSpDataObject.END_OF_FETCH )
		{
			throw new CHpInvalidExchangeRateException(	"Database error: " + status.getErrorCode() + " - "
																		+ status.getErrorMessage() );
		}
		
		// Get the result table from the query
		CSpDBResultTable resultTable = result.getResultTable();
		
		// If all has gone well, there should be one row returned for each deal in the report
		if ( resultTable.getNumOfRows() != origDataObject.getNumOfRows() )
		{
			// There are either duplicate or missing rows
			throw new CHpInvalidExchangeRateException( "Duplicate or missing conversion rate rows" );
		}

