Ensuring numeric input to Salesforce

Posted by jack on September 3, 2016

A useful javascript snippet I used in a Cast Iron custom function to check any string is a number string. This is useful where the input is a string format user input that might includes invalid characters and the Salesforce entry is either a numeric field type or has validation that causes non-integer input to be rejected.  The function returns a zero if the input is not a valid integer string, to avoid errors inputting a null into a number field in salesforce – but in other circumstances it could return an empty string as indicated.

var returnValue;
var regex = new RegExp(‘^[0-9]$’);
if(regex.test(inputString)==true)returnValue=inputString;
else returnValue=’0′;  //alternatively  else returnValue =”;

return returnValue;

Comments Off on Ensuring numeric input to Salesforce
Categories: Cast Iron,Salesforce

Setting test classes to handle disabled triggers

Posted by jack on September 3, 2016

Credit: http://stackoverflow.com/questions/10809867/salesforce-check-if-apextrigger-is-active-in-test-method

I discovered this as a result of a recent incident updating a client Org. The problem was due to the change set including a SOQL query that selected using an unindexed field – this worked fine in test scripts & sandboxes, but came up with an error in Production owing to the larger (than 20k) records involved in the selection.

Because the test method covering the class/method and trigger, making the trigger inactive in the sandbox and redeploying caused the test method to fail. Initially I wrote a different test method that covered just the class method, and commented out the original test method, but then i realised that if the test could detect whether the trigger was active, I could make the test class so that it works with trigger active or inactive like this:

@isTest
private class testApp {

   /* tests the appClass and object__c triggers */
   /* this method tests both class/method and trigger together if the trigger is active…… */
   public static testMethod void appClassMethodTriggerTest(){
      ApexTrigger appClassMethodTrigger =
                                [Select Id, Status from ApexTrigger where name=’appClassMethodTrigger’];
      // only run the test if the trigger is active…
      if(appClassMethodTrigger.Status == ‘Inactive’){
        return;
      }

 else {

      //<test code for active trigger here >

   }
   //…. other test methods…
}

 

 

 

 

Comments Off on Setting test classes to handle disabled triggers
Categories: Salesforce

Polling in the Cloud and reliability

Posted by jack on July 11, 2015

Salesforce is a very reliable SAAS application, but that is not the case for all cloud based software – and even Salesforce is subject to occasional non-availability or time-out failures. so when designing integration allowance must be made for unexpected events and failures.

Salesforce applies API activity limits, so I want to avoid running queries too frequently, and yet still enable users to get their records updated from the data warehouse via Cast Iron in close to real time. An example I just completed involved the user inputting a reference number that creates a stub record, and the integration looking in the data warehouse for a matching record and updating the stub record with the actual values. It is possible for the user to input an invalid value (in which case a matching record is never found) or to input a reference that has not got into the date warehouse yet due to processing delays elsewhere.

The obvious approach to processing the user input is to use polling – so that a change in Salesforce is picked up at a short polling interval. This is fine provided service availability for all the components ( Salesforce, Cast Iron and the client’s data warehouse) are 100% available whenever users make the triggering change in Salesforce, and the matching record does already exist in the data warehouse  – but in practice that may not be the case, and we do have failures – one way to pick up the missed synchronisations is using a scheduled query as a backup.

So I generally couple a polling triggered orchestration with a twin “tickle” orchestration – all this does is look for records that  remain in the state that should have triggered the processing, and update them without changing any business values to cause the polling to pick them up again – that way I avoid having to replicate the business logic in two orchestrations.

Of course, to make this work  requires that the standard processing changes the processed record in a way we can detect, and also that there are some limits – generally I use 2 dateTime entries in salesforce to limit the records to reprocess 1. A “most recently last tried” limit to avoid retrying every unprocessed record  on every schedule – and 2. An “oldest record to process” limit to eliminate older records that have never processed correctly so we do eventually give up on them.

 

 

 

 

Comments Off on Polling in the Cloud and reliability
Categories: Cast Iron,Salesforce

Loyalty Scheme Design in Salesforce

Posted by jack on June 23, 2015

Recently I have been implementing a trade loyalty scheme in Salesforce. This is a points based scheme that awards to an individual agent so many points per $ of sales made through the sales channel. Once that agent has accumulated enough points, these can be exchanged for any of a range of reward products. Unused points expire one year after they are earned.

In this specific instance the sale may sometimes subsequently be cancelled, and so initially the points are provisionally awarded as Pending, and only after the last cancellation date is passed are they Confirmed  (& so available to be Consumed). So there are these possible status values for the points:-

  • Pending
  • Confirmed
  • Consumed
  • Expired

The model I adopted to implement this was to create a new custom object called “Points Award” with two master-detail relationships to 1. Contact and 2. The (in this case custom) Sale object.  There is also an “Agent Reward” object with two master-detail relationships to 1. Contact and 2. The (in this case custom) Reward Product object (we could have used the standard Salesforce Product object instead).

The Points Award object has these custom integer fields

  • Points Awarded
  • Points Cancelled
  • Points Expired
  • Points Consumed

There is then a calculated field Points Balance = Points Awarded – Cancelled – Expired – Consumed

A trigger/Class based on the Sale sets the Cancelled amount = Points balance should the Sales be cancelled, and a  trigger on the Agent Reward progressively consumes available points from Points Awards starting with the oldest.

Finally, a schedulable Class is used to update Points Award to

  • Move status from Pending to Confirmed as the associated Sales passes the point where it can be cancelled, and
  • Move status from Confirmed to Expired when unused Points Awards pass their Expiry date.

For Expiry, the Points Expired is set to equal the prior Points Balance – so if the Award has been partially Consumed the amount consumed is retained and the remainder of points Expired.

Comments Off on Loyalty Scheme Design in Salesforce
Categories: Salesforce

Salesforce1 – early experience shows promise….

Posted by jack on March 8, 2014

I have been configuring Salesforce1, the new mobile/tablet interface to Salesforce, for a client’s travelling sales workforce.

This is a big advance in usefuleness over the previous Salesforce Touch and Chatter mobile clients, in that we can now configure for the clients specific needs, including:-

  • Configuring the interface to include the items users want in the order they prefer
  • Setting custom actions – for example rapid lead qualification
  • Coding bespoke requirements – for example displaying nearby accounts on a Google map centred on the user’s current location

The first iteration of this will take less than 2 days to complete & test – pretty cheap compared to a custom app.

Salesforce1 works on recent versions of IO7 and Android.

Comments Off on Salesforce1 – early experience shows promise….
Categories: Salesforce

Salesforce.com – so much more than just a CRM

Posted by jack on April 26, 2013

One issue that deters organisations from adopting Salesforce is the higher cost of licences – if you do some simple arithmetic you quickly end up with a formidable number – especially when comparing Salesforce Enterprise with much of the competition. And the saas model means after spending all that money you don’t even own a copy of the software!

There are a number of reasons why this kind of comparison can be a bit misleading – for example the phrase “perpetual licence ” may legally mean effective ownership, but in practical terms unless you have support and maintenance in place (and the cost of this is normally 20 – 30% of the original box price per year)  then the value of what you purchased quickly decays – even with s&m it is easy to get trapped in  old versions of software due to the costs of upgrade – one client I worked for had a highly configured Pivotal installation that ran their business and could not upgrade it. They were running CRM without email integration!

Like much of IT, software packages of any type do not have as long a life as other assets – which is why accountants tend to write them off over many fewer years than they take for fixed plant for example.  With saas like Salesforce (other brands are available) you are always on the latest version for as long as you subscribe.

But the accounting cost of software is not the theme I wanted to address now, but how Salesforce can quickly become a key component well outside the core CRM use when integrated to other systems, and how that in turn can enrich the CRM. My example is a recent development for a client selling hotel rooms, but can apply to many other businesses.

Hotel rooms can be booked using a wide variety of rate codes; some rates can include or exclude items like breakfast or late check-out, can  be exclusive to specific consumers or travel agency groups, or special offers (effectively sales prices). The number and variety of rates is constantly changing as all those concerned in the business seek new ways to grow their business.

As the use of these rates changes, so do the information reporting requirements for monitoring performance. With several thousand rates in use, rather than individually select every rate required in every different report, the client’s data warehouse has evolved a set of reporting flags and descriptions to allow rapid production and one-point maintenance of such reports.

But the data warehouse has no user front end, so each time a new rate is required, or an existing one is moved into or out of a selected group of rates for a specific requirement, the poor data warehouse manager must be asked to adjust the values in this reference data –  and I have an aversion to being included in business processes and repeatedly doing simple dull SQL updates. Other users were asking the report writer to manually include/exclude rates to get the reports they want quickly – neglecting update of the reference data and causing different reports intended to be comparable to end up including different sets of rates.

In essence, a single table of reference data can be represented as a single object in Salesforce enterprise – so I could rapidly design a new object based on the data fields in the SQL table, using entirely drag & drop customisation. The natural candidate for the Salesforce object Name is the rate code itself (as that is the one fixed value in the table) but Salesforce does not allow imposition of the Unique constraint on object  Names, so I did have to apply a trigger to prevent duplicates.Here is the code for my trigger (Rate_Code_Description__c is the name of the custom object:-

trigger rateCodeDuplicatePreventer on Rate_Code_Description__c (before insert, before update) {

Map<String,Rate_Code_Description__c> rateCodeMap = new Map<String, Rate_Code_Description__c>();
    for (Rate_Code_Description__c rateCode : System.Trigger.new) {
    
    // Make sure we do not treat a rate code that is not changed during an update as a duplicate
    if (( rateCode.Name != null) && (System.Trigger.isInsert || (rateCode.Name !=
    System.Trigger.oldMap.get(rateCode.Id).Name))){
    // Make sure another new rateCode isn’t also a duplicate
        if (rateCodeMap.containsKey(rateCode.Name)) {
            rateCode.Name.addError(‘Duplicate rate code in the same batch’);
        } else {
                rateCodeMap.put(rateCode.Name, rateCode);
           
        }
    }
   }
    //using a single query, finad all the ratecodes with the same name
    for (Rate_Code_Description__c rateCode : [SELECT Name from Rate_Code_Description__c WHERE Name IN :rateCodeMap.KeySet()]){
    Rate_Code_Description__c newRateCode = rateCodeMap.get(rateCode.Name);
    newRateCode.Name.addError(‘This rate code already in Salesforce’);
}
}

Standard salesforce drag & drop form design allowed me to group the various checkboxes into related sections to help users manage their own rates – in this client there is good data discipline and cross-functonal cooperation, so I have not chosen to apply varying field level security – but I could have done so with a little more time.

Integration between salesforce and the data warehouse was undertaken using Cast Iron – the integration is 2 way but not symmetrical, since with the exception of the rate code (object Name) itself, we wanted all the other entries to be mastered in Salesforce, even if originating from the data warehouse table.  And we need to ensure that users get confirmation that entries put in Salesforce have correctly updated the data warehouse table.

New rates can be input in the data warehouse – this happens automatically where a new rate code is in use in incoming reservation data but does not exist in the reference data table – in this event a stub record is created using globally applicable rules to set a few flags to sensible defaults and taking a description from the first incoming reservation record. This new rate then is synched by IBM Cast Iron using a Poll Inserted Rows database activity into Salesforce. Once in salesforce a workflow rule sends an email to a responsible member of staff (identity varies according to the value of rate code input) so that person can then correct the description and set flags to the appropriate values.

The Salesforce object is connected to a second Cast Iron orchestration using a Poll Updated Objects activity – this then send the updated values back to the data warehouse table. Finally, the response from the update activity is used to update the Salesforce object in a single date field to confirm to the user that all the integration has worked correctly and their input has updated the data warehouse table.

Total time taken to develop, test and implement this is less than two days, and the job of maintaining the rate related data is put firmly where it should be – with the business users.

Now, here is the gravy – now that we have all this rate code information in Salesforce I can leverage this to improve the CRM – providing users with information on customer, partner and supplier related activities based on both this rate code data and the other objects already in Salesforce. We’ve not done that part yet, so I will write about that another day.

Jack

 

 

 

 

 

 

Comments Off on Salesforce.com – so much more than just a CRM

Albacore Ltd for Salesforce with data integration using IBM Cast Iron

Posted by jack on February 2, 2013

For 2013 Albacore Ltd is focussing on the use of IBM Cast Iron to integrate cloud applications with internal systems.

Salesforce.com is the leading Cloud application, and Albacore Ltd has over three years experience in architecting solutions that configure Salesforce and integrate to back office systems using IBM Cast Iron Live.

If you are a UK based organisation considering  Salesforce* then get in touch at enquiries@albacore.ltd.uk

*NOTE: For data integration you must have Salesforce Enterprise or Unliimited licences; small business and workgroup editions do not allow data integration.

Comments Off on Albacore Ltd for Salesforce with data integration using IBM Cast Iron

Visualforce is not hard to master…..

Posted by jack on April 30, 2012

Salesforce normally makes a decent fist of presenting data in a usable way using the standard page designs; in the SME space, I consider avoiding code where practical to be a requirement of the job (to avoid future maintenance headaches once I’ve gone), and so to date page layout has not involved Visualforce.

The client now has a requirement to present the results of an inspection. Inspections in his industry comprise a set of basic facts who/what/when/overall rating at the inspection level, and also the results of many questions, each of which belongs to a section of the inspection and has an individual weighting and assesed mark.

Rather than hard code all the questions, these are represented in a secondary record, an inspectionItem, with a Master-Detail link to the inspection. By including the section and question text as fields, one object can handle all questions.

However when the inspection is presented in Salesforce, the client would like to see the questions arranged by section (there are over 400 questions per inspection) – so the standard Salesforce layout is clearly not fit for purpose.

An additional requirement is that the Salesforce user is able to supplement the basic Inspection record (which is loaded from another system and not to be amended) with attachments and some additional field entries.

Visualforce allows me to lay the form out nicely using tabs – with a filtered list for each tab. A final “other items” tab is included to allow any items with section name not inclueded in the other tabs to be presented to the user pending amendment to the visualforce page/correction of the incoming data as relevent . I was very pleasantly surprised to discover how quickly I could work out how to layout the whole page.

Note that there is not a 1:1 ralationship between tabs and sections – each tab contains several sections – this reflects the limitation on screen width and the number and length of the section names. The entries for each section are ordered by the column Seq_no to reproduce the order in the originating system.

Here is the start of the Visualforce display page (edit page is below):-

<start>

<apex:page standardController=”Inspection__c” id=”inspection” label=”inspection” setup=”false” extensions=”inspectionExtension” sidebar=”false”>
<apex:form >
<apex:commandButton action=”{!edit}” value=”Edit”/>
</apex:form>

<apex:tabPanel >

    <apex:tab label=”General”>
        <apex:pageBlock >
            <apex:pageBlockSection title=”{!Inspection__c.Name}+” columns=”2″>
                <apex:outputField value=”{!Inspection__c.Account__c}”   />
                <apex:outputField value=”{!Inspection__c.Contact__c}”/>
                <apex:outputField value=”{!Inspection__c.Number_or_name_of_room__c}”/>
                <apex:outputField value=”{!Inspection__c.Inspection_Date__c}”/>
                <apex:outputField value=”{!Inspection__c.Checkout_date__c}”/>
                <apex:outputField value=”{!Inspection__c.Total_Quality_Score__c}” />
                <apex:outputField value=”{!Inspection__c.Demo_Salesforce_Added__c}”/>
            </apex:pageblockSection></apex:pageBlock>
          <br/>
          Comments:<i> {!Inspection__c.Inspection_Comments__c}</i>
          <br/>
          <apex:relatedList subject=”{!Inspection__c}” list=”NotesAndAttachments” />
        <apex:pageBlock title=”Quality scores” >
            <apex:pageBlockSection title=”Overall Evaluation” columns=”2″>
                <apex:outputField value=”{!Inspection__c.Overall_Service__c}”   />
                <apex:outputField value=”{!Inspection__c.Overall_Staff__c}”   />
                <apex:outputField value=”{!Inspection__c.Overall_Accommodation__c}”   />
                <apex:outputField value=”{!Inspection__c.Overall_Food__c}”   />
                <apex:outputField value=”{!Inspection__c.Overall_Housekeeping__c}”   />       
            </apex:pageblockSection>
<apex:pageBlockSection title=”General Comments” columns=”1″>
                <apex:outputField value=”{!Inspection__c.Is_the_hotel_a_worthy_member_of_XYZ__c}”   />
                <apex:outputField value=”{!Inspection__c.Left_in_doubt_is_a_Small_Luxury_Hotel__c}”   />
                <apex:outputField value=”{!Inspection__c.Would_book_again__c}”   />
                <apex:outputField value=”{!Inspection__c.Would_encourage_selecting_another_XYZ__c}”   />
                <apex:outputField value=”{!Inspection__c.Confidence_in_Hotel__c}”   />    
                <apex:outputField value=”{!Inspection__c.Further_Comments__c  }”   />   

            </apex:pageblockSection>

        </apex:pageblock>
    </apex:tab>
    <apex:tab label=”Pre-Stay”>
        <apex:pageblock >
            <apex:pageBlockSection title=”Pre-Stay Summary” columns=”2″>
                <apex:outputField value=”{!Inspection__c.XYZ_Directory_and_Website__c}”   />
                <apex:outputField value=”{!Inspection__c.XYZ_Directory_and_Website_Comments__c}”   />      
                <apex:outputField value=”{!Inspection__c.XYZ_Membership_Registration_Procedure__c}”   />
                <apex:outputField value=”{!Inspection__c.XYZ_Membership_Registration_Comments__c}”   />
                <apex:outputField value=”{!Inspection__c.XYZ_Central_Reservation__c}”   />
                <apex:outputField value=”{!Inspection__c.XYZ_Central_Reservation_Comments__c}”   />
            </apex:pageblockSection>
            <apex:pageBlockSection title=”Items” columns=”1″ >
<apex:pageBlockTable value=”{!XYZDirectory}” var=”items”>
<apex:facet name=”header”>XYZ Directory</apex:facet>
<apex:column value=”{!items.Q_ID__c}”/>
<apex:column value=”{!items.Name}” width=”40%”/>
<apex:column value=”{!items.Mark__c}”/>
<apex:column value=”{!items.XYZ_Value__c}”/>
<apex:column value=”{!items.XYZ_Value_earned__c }”/>
<apex:column value=”{!items.Item_comments__c }” width=”30%” />
</apex:pageBlockTable>
<apex:pageBlockTable value=”{!XYZWebsite}” var=”items”>
<apex:facet name=”header”>XYZ Website</apex:facet>
<apex:column value=”{!items.Q_ID__c}”/>

<continues…>

And here is the extension to the standard controller:-

<start>

public class inspectionExtension {
private final Inspection__c item;
// The extension constructor initializes the private member
// variable acct by using the getRecord method from the standard
// controller.
public inspectionExtension(ApexPages.StandardController stdController) {
this.item = (Inspection__c )stdController.getRecord();
}

public String getId() {
return  item.id ;
}

// first list tab contents
public list<Inspections_Item__c> getXYZDirectory() {
return [select  Id,Q_ID__c, Name,Mark__c,XYZ_Value__c,XYZ_Value_earned__c,Item_comments__c  from Inspections_Item__c  Where (section__c = ‘XYZ Directory’) and Inspection__c = :item.id ORDER BY Seq_No__c  ];
}
public list<Inspections_Item__c> getXYZWebsite() {
return [select  Id,Q_ID__c, Name,Mark__c,XYZ_Value__c,XYZ_Value_earned__c,Item_comments__c  from Inspections_Item__c  Where (section__c = ‘XYZ WEBSITE www.XYZ.com’) and Inspection__c = :item.id ORDER BY Seq_No__c  ];
}

<continues….. ending>

//catch-all tab for new sections or data with inaccurate section name

public list<Inspections_Item__c> getOtherItems() {
return [select  Id,Q_ID__c, Name,Mark__c,XYZ_Value__c,XYZ_Value_earned__c,section__c,Item_comments__c  from Inspections_Item__c Where (section__c <> ‘XYZ Directory’) and
(section__c <> ‘XYZ WEBSITE www.XYZ.com’) and (section__c <> ‘Membership Registration and Web Reservation’)  and
(section__c <> ‘XYZ Central Reservations’) and (section__c <> ‘Arrival’) and
(section__c <> ‘Bedroom’) and (section__c <> ‘Bathroom’) and  (section__c <> ‘Housekeeping Bathroom’) and (section__c <> ‘Housekeeping Bedroom’) and
(section__c <> ‘Telephone / Switchboard / Messages’) and (section__c <> ‘Concierge Services’) and
(section__c <> ‘Reception/Entrance hall’) and  (section__c <> ‘Lounge room/Seating area’) and (section__c <> ‘Staircases/Lifts/Corridors’) and  (section__c <> ‘Toilets’) and
(section__c <> ‘External Grounds’) and (section__c <> ‘Pool & Beach Service’) and (section__c <> ‘Leisure/Spa facilities’) and
(section__c <> ‘Main Restaurant’) and (section__c <> ‘Room Service’) and  (section__c <> ‘Bar/Lounge’) and  (section__c <> ‘Breakfast’) and
(section__c <> ‘Departure’) and   (section__c <> ‘XYZ Brand Values’) and
Inspection__c = :item.id ORDER BY Section__c, Seq_No__c  ];
}

}

Here is  the Visualforce edit page (note – fields not settled for final design):-

<apex:page standardController=”Inspection__c” id=”inspection” label=”inspection” setup=”false” extensions=”inspectionEdit” sidebar=”false”>
<apex:form >
        <apex:pageBlock >
             <apex:pageBlockSection title=”{!Inspection__c.Name} – Upload Inspection Files” columns=”3″>
        <apex:inputFile size=”60″ value=”{!attach.body}” filename=”{!attach.name}”/><br/>
        <apex:commandButton value=”Upload” action=”{!upload}”/>
</apex:pageblockSection>

            <apex:pageBlockSection title=”Inspection Details” columns=”2″>
            <apex:commandButton action=”{!save}” value=”Save Update”/>
                <apex:outputField value=”{!Inspection__c.Id}”   />                
                <apex:outputField value=”{!Inspection__c.Account__c}”   />
                <apex:outputField value=”{!Inspection__c.Contact__c}”/>
                <apex:outputField value=”{!Inspection__c.Number_or_name_of_room__c}”/>
                <apex:outputField value=”{!Inspection__c.Inspection_Date__c}”/>
                <apex:outputField value=”{!Inspection__c.Checkout_date__c}”/>
                <apex:outputField value=”{!Inspection__c.Total_Quality_Score__c}” />
                <apex:inputField value=”{!Inspection__c.Demo_Salesforce_Added__c}”/>
            </apex:pageblockSection></apex:pageBlock>
          <br/>
          Comments:<i> {!Inspection__c.Inspection_Comments__c}</i>
          <br/>
        <apex:pageBlock title=”Quality scores” >
            <apex:pageBlockSection title=”Overall Evaluation” columns=”2″>
                <apex:outputField value=”{!Inspection__c.Overall_Service__c}”   />
                <apex:outputField value=”{!Inspection__c.Overall_Staff__c}”   />
                <apex:outputField value=”{!Inspection__c.Overall_Accommodation__c}”   />
                <apex:outputField value=”{!Inspection__c.Overall_Food__c}”   />
                <apex:outputField value=”{!Inspection__c.Overall_Housekeeping__c}”   />       
            </apex:pageblockSection>
<apex:pageBlockSection title=”General Comments” columns=”1″>
                <apex:outputField value=”{!Inspection__c.Is_the_hotel_a_worthy_member_of_XYZ__c}”   />
                <apex:outputField value=”{!Inspection__c.Left_in_doubt_is_a_Small_Luxury_Hotel__c}”   />
                <apex:outputField value=”{!Inspection__c.Would_book_again__c}”   />
                <apex:outputField value=”{!Inspection__c.Would_encourage_selecting_another_XYZ__c}”   />
                <apex:outputField value=”{!Inspection__c.Confidence_in_Hotel__c}”   />    
                <apex:outputField value=”{!Inspection__c.Further_Comments__c  }”   />   
         </apex:pageblockSection>
</apex:pageblock>
</apex:form>
</apex:page>

And here is the InspectionEdit extension:-

public with sharing class inspectionEdit {
private final Inspection__c item;
// The extension constructor initializes the private member
// variable acct by using the getRecord method from the standard
// controller.
public inspectionEdit(ApexPages.StandardController stdController) {
this.item = (Inspection__c )stdController.getRecord();
}

public String getId() {
return  item.id ;
}

   public String parentId {get;set;}
    public Attachment attach {get;set;}

    {
        attach = new Attachment();
    }
    //When user clicks upload button on Visualforce Page, perform upload/insert
    //Redirect user to newly inserted document
    public ApexPages.Pagereference upload()
    {

        //This shows how to insert an Attachment
        attach.ParentId = item.id;
        insert attach;

        return new ApexPages.Standardcontroller(attach).view();  
    }
}

 

 

Comments Off on Visualforce is not hard to master…..
Categories: Salesforce

Feeling pleased with myself…….. some salesforce automation to benefit users

Posted by jack on March 3, 2012

I have just completed a new integration that will really help my users – always gives me a warm glow(!). But there is nothing tricky here – it is really straightforward and took just a few hours to set up.

The issue was this – in salesforce we have accounts that include  IATA code(s) for each travel agent, and in the bookings data loaded to the data waerehouse  we have the IATA code for the booking agent. So where we have a matching acount, the booking data is summarised and loaded against the appropriate agent in salesforce.

The problem was that not all agents were represented in Salesforce, and of those that were, not all had all their IATA codes recorded. So the reservation data for the missing agents did not get loaded against the account.

I had been lobbying the users to get them to create the missing accounts, and has sent them lists of missing accounts to create but this client runs a tight ship and there are no back-office staff with nothing better to do than create account records – so it just wasn’t happening.

The penny finally droppped for me on thursday – if I can produce lists for them, surely I can create the account records with automation? So that is what I have just implemented, and it works like this.

1. A data warehouse query selects a list of missing IATA numbers and details dragged from the reservation transactions into a table where a. The IATA code is not represented in salesforce and b. the total level of bookings has exceeded a set threshold (high at present to handle the backlog in priority order). The reservaton details include address and name of the agent.

2. Cast Iron picks up the data from this table and creates Account objects in salesforce – to avoid problems with duplciation, the IATA code is a business key in salesforce so any pre-exisitng IATA code is rejected and such errors logged in salesforce (this can happen if the Cast Iron integration failed to replicate the salesforce acocunt record to the data warehouse in the past – not unknown).

3. A workflow rule assigns the new account on the basis of source and business region to the specified user, and sends that user an email – thanks to the nice way Salesforce uses record IDs in URLs, the email can include a direct link to the salesforce account record.

And that’s it! None of this took very long to do, but the result is that an arduous administrative task (that wasn’t being done anyway)  has been replaced with a reliable automated process.

 

 

 

Comments Off on Feeling pleased with myself…….. some salesforce automation to benefit users
Categories: Cast Iron,Salesforce

Some Cast Iron X-Path tips for the inexpert

Posted by jack on March 3, 2012

One useful thing in Cast Iron is the use of X-Path predicates to filter a variable – the trouble is I dont find the  X-Path filter syntax particularly intuitive – so this post is for my benefit for next time. After a lot of messing about I found this works to filter an XML dataset on a boolean entry

/results/result[ *:success=’false’  ]

Where /results/result is the record to be filtered and *:success=’false’ is the entry I input by right-clicking in a Map Variables activity on the target occurrence and selecting filter recurring nodes.

( /results/result is the output from a Salesforce Create Objects activity – what I am doing here is filtering to the failed Create actions to insert an error record in a custom ErrorLog__c object in salesforce ).

 

 

 

 

Comments Off on Some Cast Iron X-Path tips for the inexpert
Categories: Cast Iron,Salesforce