Example: Creating a custom process app for Salesforce opportunity management
Overview
In an opportunity management process, the first step is to create an opportunity in Salesforce, which is then followed by series of processes or process variants. The data of the closed opportunity is then stored and analyzed for the future course of actions or reference.
In this example, you create a custom process app, which is named Salesforce Opportunities, to extract, transform, and load the data from a Salesforce opportunity management process. The Python script for the Salesforce Opportunities process
app must extract data of events in the OpportunityCreation
and OpportunityChange
processes. In addition, the data must have the following event attributes:
opportunityId
activity
startTime
resource
opportunityAmount
opportunityStage
Prerequisites
Before you start creating a custom process app, you must do the following mandatory configurations:
-
Download the backup file of a process in IBM Process Mining for mapping data in
IDP
format. -
Create an account in the Salesforce instance of your organization. Remember the username and password for future use in Process App.
-
Connect your IBM Process Mining with the Salesforce account by using the Salesforce REST API through Connected App in Salesforce. You can use the OAuth parameters, consumer key, and consumer secret to validate the connection between IBM Process Mining and Salesforce. For more information about creating a Connected App in Salesforce, see Create a Connected App.
Procedure
The creation of a custom process app includes the following activities:
- Creating Summary card
- Upload the Process App logic
- Define user inputs
- Provide user guidance
- Set up a process for the process app
Creating Summary card
In the Summary card step, do the following steps:
-
Enter the name of the process app, 'Salesforce Opportunities', in the Name field.
-
Enter a description about the process app in the Brief description field.
-
Switch the toggle to Closed source or Open source to set the permissions for the process app users. You can choose one of the following options per your requirement:
-
Switch the toggle to Open source if you want to give the Use, View, Export, and Duplicate permissions to the process app users.
-
Switch the toggle to Closed source if you want to give only the Use and Export permissions to the process app users.
ⓘ Tip: For more information about permissions in process app, see the Understanding types of users and permissions in custom process app topic.
-
-
Click the Next button to go to the Upload your Process App logic step.
ⓘ Important: Optional: If you want to customize icon and add feature highlights and documentation link on the summary card, expand the Customization section. For more information about the Customization section, see the Creating a Summary card for a process app topic.
Upload the Process App logic
In the Upload your Process App logic step, you connect to Salesforce, collect data, and prepare it to be used by IBM Process mining. By clicking the Download template link, you can download the template to write a single Python script to extract and transform the data.
The following steps give the detailed instruction about writing a Python script for the Salesforce opportunity management process:
-
Define the import libraries:
import requests import json from requests.models import PreparedRequest from process_app import ProcessAppException
-
Include a method named
execute
that contains the parametercontext
:def execute(context):
ⓘ Tip: The
context
parameter contains all the contextual information that you need to write the Python script.
-
Use the OAuth parameters that you configured in Connected App and the Salesforce account credentials to set up the connection between process app and Salesforce:
# reading data from accelerator config consumerKey = config['<consumerKey>'] secret = config['<consumerSecret>'] username = config['<username>'] password = config['<password>']
-
Define exception message for unsuccessful connection between process app and Salesforce:
if consumerKey=='' or secret =='' or username =='' or password =='': raise ProcessAppException('configuration not valid')
-
Define the Salesforce account log in process and exceptions:
# login to salesforce loginParams = {'client_id': consumerKey,'client_secret': secret, 'grant_type': 'password','username':username, 'password':password} req = PreparedRequest() req.prepare_url('https://login.salesforce.com/services/oauth2/token', loginParams) loginResponse = requests.post(req.url) if loginResponse.status_code != 200 : raise ProcessAppException('cannot log to salesforce account') data = loginResponse.json() accessToken = data.get('access_token') instanceUrl = data.get('instance_url')
-
Define the event logs that needs to be extracted:
events = [] importOpportunityCreation(accessToken, instanceUrl, events) importOpportunityChange(accessToken, instanceUrl, events)
-
Define the event attributes that must be returned in the extracted data:
df = pd.DataFrame(events, columns= ['opportunityId', 'activity', 'startTime', 'resource', 'opportunityAmount', 'opportunityStage']) return df
-
Define the SOQL query in Salesforce to extract data:
def runSOQLQuery(query, accessToken, instanceUrl, nextRecordsUrl=None): req = PreparedRequest() if nextRecordsUrl != None: req.prepare_url(instanceUrl+nextRecordsUrl, {} ) else: req.prepare_url(instanceUrl+'/services/data/v39.0/query/', {'q' : query}) queryResponse = requests.get(req.url, headers={'Authorization': 'Bearer ' + accessToken}) if queryResponse.status_code != 200 : raise Exception('cannot perform SOQL query to salesforce account') return queryResponse.json()
-
Define the SOQL query to extract and transform the data tables for
OpportunityCreation
:def formatDate(date): return date[0: date.find('.')].replace('T', ' ') def importOpportunityCreation(accessToken, instanceUrl, events, nextRecordsUrl=None): queryResults = runSOQLQuery('Select Id, Amount, StageName, CreatedBy.Name from Opportunity ', accessToken, instanceUrl, nextRecordsUrl) records = queryResults.get('records') for record in records: resource = record.get('CreatedBy').get('Name') createDate = formatDate(record.get('CreatedDate')) amount = record.get('Amount') events.append([ record.get('Id'),'Create Opportunity', createDate, resource, '0' if 'null' == amount else amount, record.get('StageName') ]) if queryResults.get('nextRecordsUrl') != None : importOpportunityCreation(accessToken, instanceUrl, events, queryResults.get('nextRecordsUrl'))
-
Define the SOQL query to extract and transform the data tables for
OpportunityChange
:def importOpportunityChange(accessToken, instanceUrl, events, nextRecordsUrl=None): queryResults = runSOQLQuery('Select OpportunityId, CreatedBy.Name, CreatedDate,' + ' Field, NewValue, OldValue from OpportunityFieldHistory', accessToken, instanceUrl, nextRecordsUrl) records = queryResults.get('records') for record in records: resource = record.get('CreatedBy').get('Name') opportunityId = record.get('OpportunityId') createDate = formatDate(record.get('CreatedDate')) field = record.get('Field') if 'StageName' == field: activity='Set Opportunity to ' + record.get('NewValue') elif 'Owner' == field: activity='Change opportunity Owner' elif 'AccountId' == field: activity = 'Change opportunity Account' elif 'Amount' == field: activity= 'Change opportunity Amount' events.append([ opportunityId, activity, createDate, resource, 0, ‘’ ]) if queryResults.get('nextRecordsUrl') != None : importOpportunityChange(accessToken, instanceUrl, events, queryResults.get('nextRecordsUrl'))
You can copy and use the following code snippet that contains the complete Python script:
import requests
import json
from requests.models import PreparedRequest
from process_app import ProcessAppException
def execute(config):
# reading data from accelerator config
consumerKey = config['consumerKey']
secret = config['consumerSecret']
username = config['username']
password = config['password']
if consumerKey=='' or secret =='' or username =='' or password =='':
raise ProcessAppException('configuration not valid')
# login to salesforce
loginParams = {'client_id': consumerKey,'client_secret': secret,'grant_type': 'password','username':username,'password':password}
req = PreparedRequest()
req.prepare_url('https://login.salesforce.com/services/oauth2/token', loginParams)
loginResponse = requests.post(req.url)
if loginResponse.status_code != 200 :
raise ProcessAppException('cannot log to salesforce account')
data = loginResponse.json()
accessToken = data.get('access_token')
instanceUrl = data.get('instance_url')
events = []
importOpportunityCreation(accessToken, instanceUrl, events)
importOpportunityChange(accessToken, instanceUrl, events)
df = pd.DataFrame(events,columns= ['opportunityId', 'activity', 'startTime', 'resource',
'opportunityAmount', 'opportunityStage'])
return df
# SOQL query to extract data
def runSOQLQuery(query, accessToken, instanceUrl, nextRecordsUrl=None):
req = PreparedRequest()
if nextRecordsUrl != None:
req.prepare_url(instanceUrl+nextRecordsUrl, {} )
else:
req.prepare_url(instanceUrl+'/services/data/v39.0/query/', {'q' : query})
queryResponse = requests.get(req.url,
headers={'Authorization': 'Bearer ' + accessToken})
if queryResponse.status_code != 200 :
raise Exception('cannot perform SOQL query to salesforce account')
return queryResponse.json()
# retrieve data for opportunity creation
def formatDate(date):
return date[0: date.find('.')].replace('T', ' ')
def importOpportunityCreation(accessToken, instanceUrl, events, nextRecordsUrl=None):
queryResults = runSOQLQuery('Select Id, Amount, StageName, CreatedBy.Name from Opportunity ', accessToken, instanceUrl, nextRecordsUrl)
records = queryResults.get('records')
for record in records:
resource = record.get('CreatedBy').get('Name')
createDate = formatDate(record.get('CreatedDate'))
amount = record.get('Amount')
events.append([ record.get('Id'),'Create Opportunity', createDate, resource, '0' if 'null' == amount else amount, record.get('StageName') ])
if queryResults.get('nextRecordsUrl') != None :
importOpportunityCreation(accessToken, instanceUrl, events, queryResults.get('nextRecordsUrl'))
# retrieve data for opportunity change
def importOpportunityChange(accessToken, instanceUrl, events, nextRecordsUrl=None):
queryResults = runSOQLQuery('Select OpportunityId, CreatedBy.Name, CreatedDate,' + ' Field, NewValue, OldValue from OpportunityFieldHistory', accessToken, instanceUrl, nextRecordsUrl)
records = queryResults.get('records')
for record in records:
resource = record.get('CreatedBy').get('Name')
opportunityId = record.get('OpportunityId')
createDate = formatDate(record.get('CreatedDate'))
field = record.get('Field')
if 'StageName' == field:
activity='Set Opportunity to ' + record.get('NewValue')
elif 'Owner' == field:
activity='Change opportunity Owner'
elif 'AccountId' == field:
activity = 'Change opportunity Account'
elif 'Amount' == field:
activity= 'Change opportunity Amount'
events.append([ opportunityId, activity, createDate, resource, 0, ‘’ ])
if queryResults.get('nextRecordsUrl') != None :
importOpportunityChange(accessToken, instanceUrl, events,
queryResults.get('nextRecordsUrl'))
After you write the script and save the Python file (.py
file) in your system, click the Drag and drop file here to click to upload link in the Upload your Process App logic step to select and
upload the Python file in the "Custom process app" wizard.
When you finish uploading the Python file, click the Next button to go to the Define user inputs step in the "Custom process app" wizard.
Define user inputs
In the Define user inputs step, you set up the input data fields that the Process App users must use to specify the data to be transformed during process generation. This step is optional. For more information about the Define user inputs step, see the Defining the user input fields topic.
Click the Next button to go to the Provide user guidance step.
Provide user guidance
In the Provide user guidance step, you create user guides for your custom process app. This step is optional. For more information about the Provide user guidance step, see the Providing the user guidance for the process app topic.
Click the Next button to go to the Process settings step.
Set up a process for the process app
The Process setting step is the last step in process app creation. In this step, you upload a process back up file in the .idp
format or duplicate settings from an existing process in IBM Process Mining. The process
app uses the mapped data columns, the process settings, filters, and dashboards from the process backup file to generate a new process.
To upload a process back file, do the following steps:
-
Select the Upload process backup file option in the Process setting step.
-
Click on the Drag and drop file here or click to upload link to upload the process backup file.
-
Click the Save button.
To duplicate the settings of an existing process, do the following steps:
-
Select the Duplicate settings from a process option in the Process setting step.
-
Select a process from the Find a process dropdown.
-
Click the Create button.
If you do not have a process backup file, you can create a .csv
file with the extracted columns, which are opportunityId
, activity
, startTime
, resource
, opportunityAmount
,
and opportunityStage
. You can use the .csv
file to create a process in IBM Process Mining, for which you can create a backup file in the .idp
format.
Result
After you complete the creation of the custom process app, you can see the Salesforce Opportunities process app in the Process Apps dashboard. You can now use the process app to generate a process in IBM Process Mining. For more information about generating a process by using a process app, see the Generating a process by using the custom Process App topic.
'Figure 1. Salesforce Opportunities process model' shows the process model that you can generate by using the Saleforce Opportunities Process App.
Figure 1. Salesforce Opportunities process model