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()) {
updateCurrencies(JSON.serialize(currencyToUpdate));
}
}
@future(callout=true)
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');
req.setMethod('POST');
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}]}
//{"hasErrors":false,"results":[{"statusCode":204,"result":null}]}
}
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.setMethod('POST');
req.setTimeout(60000);
req.setEndpoint(URL.getSalesforceBaseUrl().toExternalForm()+'/services/Soap/u/35.0');
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());