Sunday, January 22, 2017

Currency Conversion in Multicurrency Org.g

In organizations with multicurrency enabled, sometimes we desperately need a way to create formulas that convert an amount from one currency to another using the current exchange rate configured in the system.
It is absolutely annoying to find that the currency conversions are not made available for Salesforce formulas.
Given below is a sample scenario and the solution using a small piece of Apex.

Scenario:

A multicurrency organization with many accounts.
The account currency could be any of the active currencies.
Any account can have a number of contracts associated with it. These contracts could be for any regions in the world making it be in any currencies (also an active currency in the system).
Imagine the Account is in USD and it has four different contracts; one for Europe(in EUR), one for Japan (in JPY) , one for UK(in GBP) and one in USA (in USD). Each of the individual contracts will display the contract values in the records currency and in user currency; also possibly in the corporate currencies in any reports.
But if you want to see the Total contracts' value of the all the 4 contracts on the account to see the Account's worth, it is not possible to create a roll up summary; remember the Contract to Account relationship is just a look-up in Salesforce.
This demands that we need to convert each of the contract amounts from their currencies to USD, which is the Account currency, in order to sum it up and store it on the account record.

Solution:

Add trigger on the Contract to invoke the below given trigger handler class that convert the currencies and store it on the account. This solution creates a map of currency and exchanges rates in the memory and uses it for every conversion. The convertCurrency method can be used in any similar situations for currency conversions.
PS: This code snippet shows only the update trigger. This has to be extended for delete and undelete too. However, in case of the Contracts, an Insert trigger is not needed as it the contract will be created as draft always. So the update on the status will trigger the update trigger.

Contract Trigger:
trigger ContractTrigger on Contract (after update) {
ContractTriggerHandler conTriggerHandler = new ContractTriggerHandler();
}
        if(trigger.isAfter && trigger.isUpdate){
            conTriggerHandler.handleAfterUpdate(trigger.newMap,trigger.oldMap);
        }
}

Trigger handler class.

public class ContractTriggerHandler {
      Map<String, Double> CurrencyRates  =  new Map <String, Double>();

      public ContractTriggerHandler() {
            for (CurrencyType cRates : [SELECT ISOCode, ConversionRate FROM CurrencyType WHERE IsActive=TRUE]) {
                  CurrencyRates.put(cRates.IsoCode, cRates.ConversionRate);
            }
      }

      public  void handleAfterUpdate(Map<id,Contract> newMap,Map<id,Contract> oldMap){
            List<Contract> ConListToProcess = new List<Contract>();
            for ( Id ContractId : newMap.keySet() ){
                  if ( newMap.get(ContractId).Annual_Value__c != oldMap.get(ContractId).Annual_Value__c || newMap.get(ContractId).Status != oldMap.get(ContractId).Status) {
                        ConListToProcess.add(newMap.get(ContractId));
                  }
            }

            If(ConListToProcess.size() > 0)
                  updateAccountClassification(ConListToProcess);
      }

      public  void updateAccountClassification(List<Contract> ContractsList ){
            Decimal TotalContractValue = 0;
            Set<id> accIds = new Set<id>();
            List <Account> accListToUpdate = new List <Account>();

            for (Contract Contracstobject : ContractsList) {
                  accIds.add(Contracstobject.AccountId);
            }

            List <Account> accList = new List <Account> ([SELECT CurrencyIsoCode, Total_Contract_Value__c, OwnerId, (SELECT CurrencyIsoCode,Annual_Value__c from Contracts where Status='Activated') from Account WHERE Id in :accIds]);
           
            for( Account accObject: accList ) {
                  TotalContractValue = 0;
                  for (Contract contractlist : accObject.Contracts) {
                        if (contractlist.CurrencyIsoCode == accObject.CurrencyIsoCode) {
                              TotalContractValue = TotalContractValue + contractlist.Annual_Value__c;
                        } else {
                              TotalContractValue = TotalContractValue + convertCurrency(contractlist.CurrencyIsoCode,accObject.CurrencyIsoCode,contractlist.Annual_Value__c);
                        }
                  }
                  accObject.Total_Contract_Value__c = TotalContractValue;

                                                }
                  accListToUpdate.add(accObject);
            }

            update accListToUpdate;
      }

      public Double convertCurrency(String inCurrency, String outCurrency, Decimal inAmount ) {
            Double inCurrencyRate  = CurrencyRates.get(inCurrency);
            Double outCurrencyRate = CurrencyRates.get(outCurrency);

            Double amountInCorpCurr = inAmount/inCurrencyRate;
            Double outAmount = amountInCorpCurr *  outCurrencyRate;

            return outAmount;
      }
}



The key method here is convertCurrency.
This method has the following signature
public Double convertCurrency(String inCurrency, String outCurrency, Decimal inAmount ). The method accepts an InCurrency and OutCurrency and the Amount. It converts the Amount in InCurrency to an Amount in InOutCurrency and returns to the caller. This is done by converting the amount to the corporate currency and then converting the corporate currency to the OutCurrency.

1 comment:

ganyatachibana said...

The 7 best casinos in Michigan - JTG Hub
The 7 best casinos in Michigan · 1. Encore at 파주 출장마사지 Wynn, 전주 출장마사지 MI · 2. DraftKings at Borgata Hotel Casino & Spa, Atlantic City, 광명 출장샵 NJ · 3. 양산 출장샵 Beau 여주 출장마사지 Rivage at

Lead/Case assignment rules reassigns the assigned leads/cases from the individual owner to the Queue.

Lead/Case assignment rules reassigns the assigned leads/cases from the individual owner to the Queue. Assignment rules in Salesforce a...