You need to sign in to do that
Don't have an account?
Jaymin Sutarwala 7
Map values getting overridden
Hi all,
A very strange issue is happening in the below apex code. I have return a batch job (Batch_Annual_Contract_Value_DER) to update Annual contract Value records with the latest Dated Exchange rates based on the Opportunity close date. The other class CurrencyUtil creates a yearExchngRateMap (Map<Integer, Map<String, Decimal>>) which holds the year as the key and another map with IsoCode as key and the Dated Exchange rate for that year as value. In the CurrencyUtil class when the getPrevYearCurrencyRate method is called from the Else at line 115 the values in the map yearExchngRateMap get overriden. I spent a lot of time on this but am not able to figure out why is this happening. Can anyone take a look at the code and see if they can figure out something.
A very strange issue is happening in the below apex code. I have return a batch job (Batch_Annual_Contract_Value_DER) to update Annual contract Value records with the latest Dated Exchange rates based on the Opportunity close date. The other class CurrencyUtil creates a yearExchngRateMap (Map<Integer, Map<String, Decimal>>) which holds the year as the key and another map with IsoCode as key and the Dated Exchange rate for that year as value. In the CurrencyUtil class when the getPrevYearCurrencyRate method is called from the Else at line 115 the values in the map yearExchngRateMap get overriden. I spent a lot of time on this but am not able to figure out why is this happening. Can anyone take a look at the code and see if they can figure out something.
public without sharing class CurrencyUtil { public static Map<String, Decimal> currencyExchangeRateMap = new Map<String, Decimal>(); // Map that holds the Exchange Rates by Year Public static Map<Integer, Map<String, Decimal>> yearExchngRateMap = new Map<Integer, Map<String, Decimal>>(); private static String CurrencyTypes = Label.Currency_Types; private static String[] currencies = CurrencyTypes.split(','); public static Decimal convertToOrgDefaultCurrency(Decimal amount, String isoCode) { Decimal convertedAmount = 0.0; if(amount != null && isoCode != null && !isoCode.equals('')) { convertedAmount = amount / currencyExchangeRateMap.get(isoCode); } return convertedAmount; } public static Decimal convertToOrgDefaultCurrency(Decimal amount, String isoCode, Integer fiscalYear) { Decimal convertedAmount = 0.0; if(amount != null && isoCode != null && !isoCode.equals('')) { convertedAmount = amount / yearExchngRateMap.get(fiscalYear).get(isoCode); } return convertedAmount; } public static void updateExchangeRatesMap() { currencyExchangeRateMap.clear(); currencyExchangeRateMap.put('USD', 1.000000); List<DatedConversionRate> dtdExchngRates = [Select Id, ConversionRate, IsoCode, NextStartDate, StartDate from DatedConversionRate where startdate = THIS_FISCAL_YEAR order by startdate desc]; System.debug('dtdExchngRates : '+dtdExchngRates); for(DatedConversionRate dtdER : dtdExchngRates) { if(!currencyExchangeRateMap.containsKey(dtdER.IsoCode)) { currencyExchangeRateMap.put(dtdER.IsoCode, dtdER.ConversionRate); } } System.debug('currencyExchangeRateMap : '+currencyExchangeRateMap); } public static void updateConversionRatesMap() { System.debug('currencies : '+currencies); yearExchngRateMap.clear(); String dteFormat = '-11-01'; Integer lstFiveYr = System.today().Year() - 5; Date fiveYrAgo = Date.valueOf(String.valueOf(lstFiveYr) + dteFormat); System.debug('fiveYrAgo : '+fiveYrAgo); List<DatedConversionRate> dtdExchngRates = [SELECT Id, ConversionRate, IsoCode, NextStartDate, StartDate FROM DatedConversionRate WHERE StartDate >=: fiveYrAgo ORDER BY Startdate asc]; System.debug('dtdExchngRates : '+dtdExchngRates); // Build the DatedConversionRateMap for the past five years - From DatedConversionRate Object if(!dtdExchngRates.isEmpty()) { Integer dcrYear = null; Map<String, Decimal> currRateMap = null; for(DatedConversionRate dcr : dtdExchngRates) { dcrYear = dcr.StartDate.Year(); if(yearExchngRateMap != null && !yearExchngRateMap.containsKey(dcrYear)) { yearExchngRateMap.put(dcrYear, new Map<String, Decimal> {'USD' => 1.000000}); } if(yearExchngRateMap != null && yearExchngRateMap.containsKey(dcrYear)) { currRateMap = yearExchngRateMap.get(dcrYear); if(currRateMap != null && !currRateMap.containsKey(dcr.IsoCode)) { currRateMap.put(dcr.IsoCode, null); yearExchngRateMap.put(dcrYear, currRateMap); } if(currRateMap != null && currRateMap.containsKey(dcr.IsoCode)) { currRateMap.put(dcr.IsoCode, dcr.ConversionRate); yearExchngRateMap.put(dcrYear, currRateMap); } } } } System.debug('yearExchngRateMap : '+yearExchngRateMap); /* Verify whether all the Currency Types are present for all the years, If not add the missed out CurrencyRate from the Previous Fiscal Year */ Decimal exchngRate = null; Map<String, Decimal> currencyRateMap = new Map<String, Decimal>(); Date fiscalYrStartDate = Date.newInstance(Date.today().year(), 11, 1); Integer fiscalYear; if(yearExchngRateMap != null && !yearExchngRateMap.isEmpty()) { // for(Integer fiscalYear : yearExchngRateMap.keyset()) { System.debug('Currencies: ' + currencies); if(Date.today() < fiscalYrStartDate) fiscalYear = Date.today().year() - 1; else fiscalYear = Date.today().year(); for(Integer iteration = 0; iteration<5; iteration++){ System.debug('Inside iteration loop, iteration# ' + iteration); System.debug('Currency List size: ' + currencies.size()); System.debug('fiscalYear: ' + fiscalYear); if(yearExchngRateMap.containsKey(fiscalYear)){ currencyRateMap = yearExchngRateMap.get(fiscalYear); System.debug('Currency Rate Map found for FY ' + fiscalYear); System.debug('Currency Rate Map: (in IF) ' + currencyRateMap); for(String curr : currencies) { if(!currencyRateMap.containsKey(curr)) { // exchngRate = getPrevYearCurrencyRate(fiscalYear, curr, fiveYrAgo.year()-1); exchngRate = getPrevYearCurrencyRate(fiscalYear, curr); System.debug('Currency (in IF): ' + curr); System.debug('Exchange Rate (in IF): ' + exchngRate); currencyRateMap.put(curr, exchngRate); } } yearExchngRateMap.put(fiscalYear, currencyRateMap); System.debug('yearExchngRateMap (in IF): ' + yearExchngRateMap); } else{ //currencyRateMap.clear(); System.debug('Currency Rate Map: (before) ' + currencyRateMap); System.debug('yearExchngRateMap (before in ELSE): ' + yearExchngRateMap); for(Integer key: yearExchngRateMap.keySet()){ System.debug('yearExchngRateMap(' + key + ')' + yearExchngRateMap.get(key)); } for(String curr : currencies) { // exchngRate = getPrevYearCurrencyRate(fiscalYear, curr, fiveYrAgo.year()-1); exchngRate = getPrevYearCurrencyRate(fiscalYear, curr); System.debug('Currency: (in ELSE)' + curr); System.debug('Exchange Rate: (in ELSE)' + exchngRate); currencyRateMap.put(curr, exchngRate); } System.debug('Currency Rate Map: (after) ' + currencyRateMap ); System.debug('Fiscal Year: ' + fiscalYear); yearExchngRateMap.put(fiscalYear, currencyRateMap); System.debug('yearExchngRateMap (after in ELSE): ' + yearExchngRateMap); for(Integer key: yearExchngRateMap.keySet()){ System.debug('yearExchngRateMap(' + key + ')' + yearExchngRateMap.get(key)); } } fiscalYear = fiscalYear - 1; } } System.debug('yearExchngRateMap : After Verification Logic : '+ yearExchngRateMap); //Display yearExchangRateMap for(Integer key: yearExchngRateMap.keySet()){ System.debug('yearExchngRateMap(' + key + ')' + yearExchngRateMap.get(key)); } } // private static Decimal getPrevYearCurrencyRate(Integer fYear, String currencyIso, Integer fiveYrAgoYear) { private static Decimal getPrevYearCurrencyRate(Integer fYear, String currencyIso) { Decimal convRate; Integer prevFiscalYear = fYear - 1; //if(prevFiscalYear > fiveYrAgoYear){ // System.debug('yearExchngRateMap (in getPrevYearCurrencyRate): ' + yearExchngRateMap); for(Integer key: yearExchngRateMap.keySet()) System.debug('yearExchngRateMap(' + key + ')' + yearExchngRateMap.get(key)); // Map<String, Decimal> prevCurrRateMap = yearExchngRateMap.get(prevFiscalYear); convRate = null; System.debug('In getPrevYearCurrencyRate'); if(prevCurrRateMap != null && prevCurrRateMap.containsKey(currencyIso)) { if(prevCurrRateMap.get(currencyIso) != null) { convRate = prevCurrRateMap.get(currencyIso); } else { //convRate = getPrevYearCurrencyRate(prevFiscalYear, currencyIso, fiveYrAgoYear); convRate = getPrevYearCurrencyRate(prevFiscalYear, currencyIso); } } else{ //convRate = getPrevYearCurrencyRate(prevFiscalYear, currencyIso, fiveYrAgoYear); convRate = getPrevYearCurrencyRate(prevFiscalYear, currencyIso); } /*} else{ convRate = null; }*/ return convRate; } }
global class Batch_Annual_Contract_Value_DER implements Database.Batchable<sObject>, Database.Stateful { Map<Id,Annual_Contract_Value__c> MapquoteId = new Map<ID,Annual_Contract_Value__c>(); Map<Id,String> MapIsoCode = new Map<ID,String>(); Map<Id,Integer> MapCloseDate = new Map<ID,Integer>(); final String[] oppStageToAvoid = new String[] {'Declined to Quote', 'Legacy'}; final String[] oppOpenStage = new String[] {'Fact Finding', 'Covering the Bases', 'Negotiating', 'Closing'}; final String[] oppClosedStage = new String[] {'Closed/Lost', 'Closed/Won'}; global List<Annual_Contract_Value__c> start(Database.BatchableContext BC) { List<Annual_Contract_Value__c> acvList = [SELECT Id,Quote__r.Id,Dated_Exchange_Rate__c,Fiscal_Year__c,Quote__c,Expected_Revenue__c,Quote__r.CurrencyIsoCode,Opportunity_Stage__c,Opportunity_Close_Date__c, Batch_Error__c, Batch_Job_runtime__c, ACV_Currency__c FROM Annual_Contract_Value__c WHERE Opportunity_Stage__c !=: oppStageToAvoid AND (Opportunity_Stage__c IN :oppOpenStage OR (Opportunity_Stage__c IN :oppClosedStage AND Opportunity_Last_Modified_Date__c = LAST_N_DAYS:31)) ORDER BY Id, Quote__c]; system.debug('acvList:'+acvList); system.debug('acvList size: ' + acvList.size()); if(acvList.size() > 0){ For(Annual_Contract_Value__c acvInstance:acvList){ MapquoteId.put(acvInstance.Quote__c,acvInstance); } } List<Quote> qList = [select Id,CurrencyIsoCode,Opportunity.CloseDate from Quote where Id IN: MapquoteId.keyset()]; System.debug('qList size: ' + qList.size()); if(qList.size() > 0){ For(Quote qInstance:qList){ MapIsoCode.put(qInstance.Id,qInstance.CurrencyIsoCode); Date fiscalYrStartDate = Date.newInstance(qInstance.Opportunity.CloseDate.year(), 11, 1); System.debug('Quote Id: ' + qInstance.Id); System.debug('fiscalYrStartDate: ' + fiscalYrStartDate); If(qInstance.Opportunity.CloseDate < fiscalYrStartDate) MapCloseDate.put(qInstance.Id,qInstance.Opportunity.CloseDate.year() - 1); Else MapCloseDate.put(qInstance.Id,qInstance.Opportunity.CloseDate.year()); } } // system.debug('Map1Values:'+MapquoteId.values()); // system.debug('Map Values:'+MapIsoCode.values()); return acvList; } global void execute(Database.BatchableContext BC, List<Annual_Contract_Value__c> scope) { // CurrencyUtil.updateExchangeRatesMap(); CurrencyUtil.updateConversionRatesMap(); List<Annual_Contract_Value__c> updateACVList = new List<Annual_Contract_Value__c>(); Decimal CUDER = 0.0; Integer currentFY; Date fiscalYrStartDate = Date.newInstance(Date.today().year(), 11, 1); System.debug('MapCloseDate size: ' + MapCloseDate.size()); if(Date.today() > fiscalYrStartDate) currentFY=Date.today().year(); else currentFY=Date.today().year() - 1; For(Annual_Contract_Value__c acvqList:scope){ System.debug('Close date year: ' + MapCloseDate.get(acvqList.Quote__c)); System.debug('Quote currency: ' + MapIsoCode.get(acvqList.Quote__c)); System.debug('ACV record id: ' + acvqList.Id); if(CurrencyUtil.yearExchngRateMap.containsKey(MapCloseDate.get(acvqList.Quote__c)) == TRUE && CurrencyUtil.yearExchngRateMap.get(MapCloseDate.get(acvqList.Quote__c)).containsKey(MapIsoCode.get(acvqList.Quote__c)) == TRUE) CUDER = CurrencyUtil.yearExchngRateMap.get(MapCloseDate.get(acvqList.Quote__c)) .get(MapIsoCode.get(acvqList.Quote__c)); else CUDER = NULL; if(CUDER == NULL && MapCloseDate.get(acvqList.Quote__c) >= currentFY) CUDER = CurrencyUtil.yearExchngRateMap.get(currentFY) .get(MapIsoCode.get(acvqList.Quote__c)); system.debug('CUDER from CurrencyUtil map: ' + CUDER); if(CUDER != NULL){ if(acvqList.Dated_Exchange_Rate__c != CUDER || acvqList.Dated_Exchange_Rate__c == NULL){ system.debug(acvqList); acvqList.Dated_Exchange_Rate__c = CUDER; acvqList.Batch_Error__c = false; acvqList.Batch_Job_runtime__c = DateTime.now(); updateACVList.add(acvqList); } } Else{ if(acvqList.ACV_Currency__c == 'USD'){ system.debug(acvqList); acvqList.Dated_Exchange_Rate__c = 1.0; acvqList.Batch_Error__c = false; acvqList.Batch_Job_runtime__c = DateTime.now(); updateACVList.add(acvqList); } else{ acvqList.Batch_Error__c = true; acvqList.Batch_Job_runtime__c = DateTime.now(); updateACVList.add(acvqList); } } } if(updateACVList.size() > 0){ Try{ system.debug('updateACVList'+updateACVList); Update updateACVList; }Catch(DMLException DMLEx){ System.debug('Exception While Updating ACV records.'+DMLEx); } } } global void finish(Database.BatchableContext BC) { } }
- Take a look at at and document what all your maps are for, and when they are updated. It looks like the culprit might be where you're repeatedly calling updateConversionRates in your batch class's execute method.
- Also, I would remove static variables from the equation (see your "Utility" class). These static variables can really confuse developers when not used properly.
- Instead of calling updatedConversionRates 1 time per BATCH, see about moving it outside of the batches and into the finish method of your batch class instead, so that it's only called once. It looks like this should work, but again, it's very difficult to read your code and what you're trying to accomplish.
In a nutshell, your values are getting overriden because you are calling "put" on the same key / value pair multiple times. Once you find that, you find your issue.Good luck.