We're using multicurrency in our project. DatedConversionRate has been updating every day by external process. The requirenment is to update CurrencyType records with most relevant rates.

At the first glance the easiest way is to build a trigger, but both objects doesn't support DML.

Please find my answer below, all comments and/or improvements are appreciated.

1 Answer 1


I've found this piece, which allows to update CurrencyType by http request. The other answer allows to get sessionId for predefined user. With help of those and building comparison of two object, scheduled class can be implemented:

global class DatedConversionRateScheduled implements Schedulable {

private static final String NS_SOAP = 'http://schemas.xmlsoap.org/soap/envelope/';
private static final String NS_SF = 'urn:partner.soap.sforce.com';
private static final String ERR_SETTING = 'Custom setting Integration__c aren\'t specified for user: integration';

global void execute (schedulableContext SC) {
    map<string,decimal> isoToRateMap = new map<string,decimal> ();
    for (DatedConversionRate rate :[select IsoCode, ConversionRate from DatedConversionRate where StartDate = today]) {
        isoToRateMap.put(rate.IsoCode, rate.ConversionRate);
    list<CurrencyTypeW> currencyToUpdate = new list<CurrencyTypeW>();
    for (CurrencyType curr :[select Id, IsoCode, ConversionRate from CurrencyType]) {
        if (isoToRateMap.containsKey(curr.IsoCode)) {
            if (curr.ConversionRate != isoToRateMap.get(curr.IsoCode)) {
                currencyToUpdate.add(new CurrencyTypeW(curr.Id, isoToRateMap.get(curr.IsoCode)));
    if (!currencyToUpdate.isEmpty()) {

private static void updateCurrencies(String body) {
    HttpResponse res;
    try {
        Http h = new Http();
        HttpRequest req = new HttpRequest();
        req.setEndpoint(URL.getSalesforceBaseUrl().toExternalForm() + '/services/data/v35.0/composite/batch');
        req.setBody('{"batchRequests":' + body + '}');
        req.setHeader('Authorization', 'OAuth ' + getSessionId());
        req.setHeader('Content-Type', 'application/json');
        res = h.send(req);
    } catch (Exception e) {
        insert new CurrencyLog__c(log__c = e.getMessage());
    insert new CurrencyLog__c(log__c = res.getBody());
    //{"hasErrors":true,"results":[{"result":[{"errorCode":"ALREADY_IN_PROCESS","message":"rsf calculation in progress."}],"statusCode":400}]}

private class ConversionRateW {
    Decimal ConversionRate;
    private ConversionRateW(Decimal rate) {
        ConversionRate = rate;

private class CurrencyTypeW {
    String method;
    String url;
    ConversionRateW richInput;
    private CurrencyTypeW (String id, Decimal rate) {
        method = 'PATCH';
        url = 'v35.0/sobjects/CurrencyType/' + id;
        richInput = new ConversionRateW(rate);

private static String getSessionId() {
    Integration__c cred = Integration__c.getInstance('integration');
    if ( Null != cred ) {
        return login(cred.login__c, cred.auth__c);
    } else {
        throw new LoginException(ERR_SETTING);
    return Null;

private static String login(String user, String password) {
    HttpRequest req = new HttpRequest();
    req.setHeader('Content-Type', 'text/xml;charset=UTF-8');        
    req.setHeader('SOAPAction', '""');
    req.setBody('<Envelope xmlns="http://schemas.xmlsoap.org/soap/envelope/"><Header/><Body><login xmlns="urn:partner.soap.sforce.com"><username>' +user+ '</username><password>' + password + '</password></login></Body></Envelope>');        
    HttpResponse res =  new Http().send(req);
    if(res.getStatusCode() != 200)
        Dom.Document responseDocument = res.getBodyDocument();
        Dom.Xmlnode rootElm = responseDocument.getRootElement(); // soapenv:Envelope
        Dom.Xmlnode bodyElm = rootElm.getChildElement('Body', NS_SOAP); // soapenv:Body 
        Dom.Xmlnode faultElm = bodyElm.getChildElement('Fault', NS_SOAP); // soapenv:Fault
        Dom.Xmlnode faultStringElm = faultElm.getChildElement('faultstring', null); // faultstring          
        throw new LoginException(faultStringElm.getText()); 
    Dom.Document responseDocument = res.getBodyDocument();
    Dom.Xmlnode rootElm = responseDocument.getRootElement(); // soapenv:Envelope
    Dom.Xmlnode bodyElm = rootElm.getChildElement('Body', NS_SOAP); // soapenv:Body 
    Dom.Xmlnode loginResponseElm = bodyElm.getChildElement('loginResponse', NS_SF); // loginResponse
    Dom.Xmlnode resultElm = loginResponseElm.getChildElement('result', NS_SF); // result
    Dom.Xmlnode sessionIdElm = resultElm.getChildElement('sessionId', NS_SF); // sessionId
    return sessionIdElm.getText();

private class LoginException extends Exception {}

where we are:

  • selecting all DatedConversionRate for most relevant date
  • compare them with all CurrencyType
  • building json body with all CurrencyType to be updated
  • sending body to salesforce itself
  • storing result in custom object

Since we are going to make callout from scheduled job, we need credentials for this. It can be achieved by creating integration user and custom setting where we can store username and his pass+token.

Custom Setting Integration__c, with fields login__c and auth__c

The result of execution can be stored in custom object:

CurrencyLog__c with long area field log__c

Finally we can test it with:

System.schedule('DatedConversionRate Job', '0 30 * * * ?', new DatedConversionRateScheduled());
  • This has been a lifesaver. I have been looking after a way to get the sessionId and have been struggling for a few days until I came across your solution. Thank you! Mar 16, 2022 at 8:37

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .