Example: Creating a custom process app for Salesforce opportunity management

What is a process app?

A process app is a set of features that helps you successfully analyze a specific use case. For example, process apps can automate the extraction, transformation and loading (ETL) of the data of a specific process mining scenario.

For more information, see Process apps.

What is a custom process app?

A custom process app is an app you can create on your own and adjust it to your particular use case. The custom process app can be used by the business analysts that want to analyze a particular case. The Process App Builder feature comes with a set of predefined analytics dashboards that cover the elements a business analyst needs to get insights into a specific use case.

To build a custom process app you need a good understanding of your use case, the process that you want to analyze and the business insights you want to highlight. Building a custom process app also requires coding skills because the ETL process (data extraction, transformation, and loading) must be provided in a Python logic file.

For more information, see Custom process apps.

Creating a custom process app for Salesforce opportunity management

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 named Salesforce Opportunities that you use 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 complete 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 your 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.

Creating a custom process app

Creating a custom process app for Salesforce opportunity management is broken up into the following steps:

  1. Accessing Create Process App wizard
  2. Creating Summary card
  3. Uploading the Process App logic
  4. Defining user inputs
  5. Providing user guidance
  6. Setting up a process for the process app

Accessing Create Process App wizard

To access the Create Process App wizard:

  1. On the Home page, click the Start from a process application tile.
  2. On the Process Apps dashboard, click Create process app to open the Create Process App wizard.

Creating Summary card

In the Summary card step, complete the following steps:

  1. Enter the name of the process app, Salesforce Opportunities, in the Name field.

  2. Enter a description about the process app in the Brief description field.

  3. 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.

    For more information about permissions in process app, see Types of users and permissions in custom process app.

  4. Optional: If you want to customize the icon and add feature highlights and documentation link on the summary card, expand the Customization section. For more information about configuring the Customization section, see Creating a Summary card for a process app.

  5. Click the Next button to go to the Upload your Process App logic step.

Upload the process app logic

In this step, you must provide a python file that will perform the data extraction and transformation. In this case, you want the code to connect to Salesforce, collect data and prepare it to be used by IBM Process Mining. You can use a code template that will help you get started with creating the Python script. To download the template, click the Download template link.

The following steps give the detailed instruction about writing a Python script for the Salesforce opportunity management process:

  1. Define the import libraries:

        import requests
        import json
        from requests.models import PreparedRequest
        from process_app import ProcessAppException
    
  2. Include a method named execute that contains the parameter context:

        def execute(context):
    

    The context parameter contains all the contextual information that you need to write the Python script.

  3. 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>']
    
  4. Define exception message for unsuccessful connection between process app and Salesforce:

        if consumerKey=='' or secret =='' or username =='' or password =='':
            raise ProcessAppException('configuration not valid')
    
  5. 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') 
    
  6. Define the event logs that needs to be extracted:

        events = []
        importOpportunityCreation(accessToken, instanceUrl, events)
        importOpportunityChange(accessToken, instanceUrl, events)
    
  7. 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
    
  8. 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()
    
  9. 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'))
    
  10. 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 Defining the user input fields.

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 Providing the user guidance for the process app.

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, complete the following steps:

  1. Select the Upload process backup file option in the Process setting step.

  2. Click on the Drag and drop file here or click to upload link to upload the process backup file.

  3. Click the Save button.

To duplicate the settings of an existing process, do the following steps:

  1. Select the Duplicate settings from a process option in the Process setting step.

  2. Select a process from the Find a process dropdown.

  3. 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 Generating a process by using the custom Process App.