Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

Battling Options in Salesforce

DZone's Guide to

Battling Options in Salesforce

The Salesforce platform offers a great deal of flexibility for its customer base. However, if not implemented carefully, a battle of options can arise.

· Integration Zone ·
Free Resource

SnapLogic is the leading self-service enterprise-grade integration platform. Download the 2018 GartnerMagic Quadrant for Enterprise iPaaS or play around on the platform, risk free, for 30 days.

Salesforce is a CRM solution that has continued to grow and prosper since the initial sales automation software was released in 1999. In the nearly twenty years of innovation, options have been included to help meet the needs of the customer base of nearly 4 million users. Some of those options are noted below:

  • Apex Programming Language (Classes and Triggers)/Visual Force Pages

  • Validation Rules

  • Workflows

  • Process Builder

  • Visual Flow

  • Lightning Components

  • RESTful/SOAP API

  • Quick Actions

These items are great features to help customers meet their CRM needs — most of the time with no/little formal application development.

However, issues can arise when multiple of these features are implemented for a given object (Lead, Account, Opportunity, etc.). This article focuses on an example I encountered on a recent project.

The Need

The customer was new to Salesforce and was planning to use the Platform as a Service (Paas) solution for their Sales group, which is certainly a common use case. A majority of the sales activities were stored in their ERP solution and were being migrated over to Salesforce and the Service Cloud product.

In order to leverage existing functionality in the ERP solution, RESTful end-points were written and exposed to allow Salesforce to obtain proprietary information as part of the Lead processing flow. One use case was to validate the Lead did not already exist in their ERP system. If the Lead's information matched an existing customer, the unique ID for that customer was returned. Otherwise, a new unique ID would be generated - keeping Salesforce in the ERP system in sync.

The Solution

Using the Apex programming language, custom code was written to make a call to the RESTful API endpoint of the ERP system. Since the request was @future   based, a flag was used (called  sentToERP__c ) in Salesforce to denote when a request was made. When the request was returned, the sentToERP__c   flag would be removed and a new flag (called  validated__c ) in Salesforce was set.

Unit tests were written in Apex and the overall coverage for all Apex classes was 98%. The only items which were not covered by unit tests were required methods when an Invoker class that weren't being used.

In order to handle other aspects of the sales process, the Salesforce Admin created workflows, validation rules and process builder functionality to the Lead object.

The Battle

During testing, errors began to surface on the integration side of the project. The error that was noted was that a "future method cannot be called..."

As a developer with 20 years of objected-oriented experience and several years of Apex-development experience (including asynchronous web callouts), I was pretty confident the design of the integration service used on this project was solid. In fact, when I executed my unit tests upon completion of the Apex development, I did not encounter any such errors. Follow-up live testing using the Salesforce client also demonstrated the code was functioning as expected.

When I reviewed the logs while reproducing the scenario found during testing, I quickly noticed that the trigger-based integration was firing as expected - but then firing three more times afterwards. The three subsequent calls yielding the errors that were referenced in the ticket. I then ran the Apex unit tests and was now seeing similar errors.

I was confused.

Then, I started looking into the logs, adding System.debug() statements in the Apex code to get a feel on why this was happening.

What turned out to be the issue was related to the workflows and process builder functionality that was added to the sales process. The changes that were made to the Lead were causing the Lead trigger to fire and the current state of the data caused additional calls into the RESTful API service. Working with the Salesforce admin, we were able to coordinate the expected conditions and update the isValid() custom Apex method used to determine if the Lead needs to be sent to the ERP system.

Conclusion

I remember the first time I ever heard of someone talking about an API. It was at an early job in my career and the API was for an application running on a mainframe. One of the senior team members from my company asked the vendor if they had an API available and explained the intended use of the API. The engineer from the vendor indicated, "Yes, we have an API. We provide the gun, you provide the foot."

That statement has resonated with me throughout my entire career.

With products like Salesforce, there are a lot of options one has to fulfill the needs of your sales team. Without a strong understanding on each component...and when to use them...your Salesforce org can become a challenge to manage and expand.

In the example above, I was able to convince the Salesforce admin to run the Apex unit tests when making changes to the Lead object — since the code coverage was set up to handle a majority of the scenarios that could exist with the Lead. Once this process was employed, unexpected errors were caught by the Salesforce admin and not found by the testing community.

Have a really great day!

Download A Buyer's Guide to Application and Data Integration, your one-stop-shop for research, checklists, and explanations for an application and data integration solution.

Topics:
salesforce ,integration ,future ,flow ,design and develop software ,paas ,erp ,restful

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}