Scheduled Apex

Custom Apex allows a developer to select which records to screen and utilizes Salesforce scheduled batches to run many thousands of records in the background.  It can be a flexible way for companies handling large volumes of records and avoids hitting limits with the Apex Flex Queue which can be an issue for proccessing large volumes via real time triggers.

Requires:

  1. A developer to write the apex classes, but once written can be automated and run in the background.
  2. CRS version 6.1 and above

How many records can be screened in bulk in this way?

Batch performance is ~ 2000 Screening Cases per hour.  Cases are first created, then updated with results and Audit Events.  To speed up the process two or more batches can run concurrently. Please note Salesforce limits orgs to 5 apex jobs executing at the same time so care should be taken to not block other asynchronous jobs.

Batches can be started by the Scheduler or Apex script in Anonymous Apex Window within Developer Console. The maximum number of records is limited by the number of records you can collect within concurrent SOQL queries: 50,000.

How do I do this?

  1. Decide on your entry criteria for your Apex.  Tip.  Remember to check to see if a screening case already exists for the record you want to screen.
  2. Create Scheduler Apex class and test, test it with the required screening conditions for your data set
  3. Run tests and ensure you have appropriate test coverage for supporting test classes
  4. Once tested to schedule, from the Apex Classes list select “Scheduled Apex” button
    1. Give the job a name
    2. Search for the apex class created in step 2
    3. Set frequency, start, end date and preferred start time
    4. Select save.
  5. Your job is now scheduled and can be managed via “Scheduled Jobs” in setup.

The code used depends on the version of Customer Risk Screener you are using and whether you have enabled advanced setup or not.  Click through for examples:

Version 6.4

Introduced a new mapping table which holds variables such as entity type, group, name transposition.  This makes the code easier to manage, just use the desired mapping Id in the objectType e.g.

        item.objectType = 'a01D300000GHddSIAT'

Example Code

Use Case 1: Screen Accounts

Checks to see if an Account created in the last 24 hours already has a Screening Case record in the tr_wc1_KYC_Screening_Case__c screening object; and if not, creates a screening case record for it.  Note you should customise the apex class to meet your organisation’s needs.

Name = “AccountScreeningBatchScheduler”:

    	
            

global class AccountScreeningBatchScheduler implements Schedulable {

    global void execute(SchedulableContext sc) {

        Set<Id> accountsWithCases = new Set<Id>();

        for (tr_wc1__KYC_Screening_Case__c cs: [SELECT Id, tr_wc1__Account__c FROM tr_wc1__KYC_Screening_Case__c WHERE CreatedDate > :System.now().addHours(-24) AND tr_wc1__Account__c != null]) {

            accountsWithCases.add(cs.tr_wc1__Account__c);

        }

        List<tr_wc1.tr_automateScreeningV2.automateScreeningVars> sCaseVars = new List<tr_wc1.tr_automateScreeningV2.automateScreeningVars>();

        for (Account acc : [SELECT Id FROM Account WHERE LastModifiedDate > :System.now().addHours(-24) AND Id != :accountsWithCases]) {

            tr_wc1.tr_automateScreeningV2.automateScreeningVars item = new  tr_wc1.tr_automateScreeningV2.automateScreeningVars();

            item.objectType = 'a01D300000GHddSIAT';

            item.recordId = acc.Id;

            sCaseVars.add(item);

        }

        tr_wc1.tr_BulkCreateAndScreenCasesV2 batch = new tr_wc1.tr_BulkCreateAndScreenCasesV2(sCaseVars);

        Database.executeBatch(batch, 30);

    }

}

Version 6.1-6.3

Variables supported by Apex

The apex can set additional variables for the case e.g. enable ongoing monitoring, or send a record to a specific group

Variable

Comment

Mandatory

enableMonitoring

Sets the Ongoing Screening Enabled flag to either TRUE or FALSE

YES

objectType

Specify the object you are screening: CONTACT, LEAD or ACCOUNT, if using a custom object: CUSTOM OBJECT

YES

recordId

Specify the ID of the record you are screening

YES

worldCheckGroupID

The ID of the WC1 Group you wish to screen the record against.  If left blank it will screen against the group setup in the Screening Setup Wizard.  See Tips for how to identify a Group ID.

*Required when screening using Custom Mapping OR if desired e.g. send accounts in  country X to group 1 but all other accounts to group 2. 

enableNameTransposition

Contacts/Individuals only.  True or False.  Name Transposition is useful if you do not know the word order of the name you are passing for the contact.  Enabling this to true will screen the name with all variants of the word order e.g. John Doe or Doe John.  Setting this may increase your hits in WorldCheck. 

NO

screeningCaseId

The Salesforce ID of the screening case

*Required when updating an existing screening case, or fetching WC1 data using the ‘SkinnyCase’ process

worldCheckOneCaseId

The World-Check equivalent ID of the record which appears in the World-Check UI.  Setting this variable tells the system which case to update.  Can be found in the Screening Case object:

Case ID: tr_wc1__Case_Id__c

*Required when updating an existing screening case, or fetching WC1 data using the ‘SkinnyCase’ process

worldCheckIneCaseSystemId

The World-Check backend ID used by in the API integration.  Setting this variable tells the system which case to update.  Can be found in the Screening Case object:

Case System ID: tr_wc1__Thomson_Reuters_Case_Id__c

*Required when updating an existing screening case.

 

 

TIP How do I identify group IDs?

Navigate to a screening case record (you may need to manually create a screening case record to access the page layout if none already exist). Click the button “Get Group Ids”. A pop up appears listing all the groups and their group ids from World-Check. Copy the IDs for the Groups you wish to use.

 

Example Code

Use Case 1: Screen Contacts

Checks to see if a Contact created in the last 24 hours already has a Screening Case record in the tr_wc1_KYC_Screening_Case__c screening object; and if not, creates a screening case record for it.  Note you should customise the apex class to meet your organisation’s needs.

Name = “ContactScreeningBatchScheduler”:

    	
            

global class ContactScreeningBatchScheduler implements Schedulable {

 

    global void execute(SchedulableContext sc) {

 

        Set<Id> contactsWithCases = new Set<Id>();

        for (tr_wc1__KYC_Screening_Case__c cs: [SELECT Id, tr_wc1__Contact__c FROM tr_wc1__KYC_Screening_Case__c WHERE CreatedDate > :System.now().addHours(-24) AND tr_wc1__Contact__c != null]) {

            contactsWithCases.add(cs.tr_wc1__Contact__c);

        }

 

        List<tr_wc1.tr_automateScreening.automateScreeningVars> sCaseVars = new List<tr_wc1.tr_automateScreening.automateScreeningVars>();

        for (Contact cont : [SELECT Id FROM Contact WHERE LastModifiedDate > :System.now().addHours(-24) AND Id != :contactsWithCases]) {

            tr_wc1.tr_automateScreening.automateScreeningVars item = new tr_wc1.tr_automateScreening.automateScreeningVars();

            item.enableMonitoring = false;

            item.objectType = 'Contact';

            item.recordId = cont.Id;

            sCaseVars.add(item);

        }

 

        tr_wc1.tr_BulkCreateAndScreenCases batch = new tr_wc1.tr_BulkCreateAndScreenCases(sCaseVars);

        Database.executeBatch(batch, 30);

    }

}

Sample supporting test class:

    	
            

@IsTest

private class ContactScreeningBatchSchedulerTest {

 

    @isTest

    static void testContactScreeningBatchScheduler() {

 

        Contact cont = new Contact(LastName = 'test');

        insert cont;

        Test.startTest();

        ContactScreeningBatchScheduler sched = new ContactScreeningBatchScheduler();

        System.schedule('Daily ContactScreeningBatchScheduler', '0 0 0 * * ?', sched);

        Test.stopTest();

        System.assertEquals(1, [SELECT Id FROM CronJobDetail WHERE Name = 'Daily ContactScreeningBatchScheduler'].size());

    }

}

Use Case 2: Screen Accounts

Checks to see if an Account created in the last 24 hours already has a Screening Case record in the tr_wc1_KYC_Screening_Case__c screening object; and if not, creates a screening case record for it.  Note you should customise the apex class to meet your organisation’s needs.

Name =   “AccountScreeningBatchScheduler”

    	
            

global class AccountScreeningBatchScheduler implements Schedulable {

 

    global void execute(SchedulableContext sc) {

 

        Set<Id> AccountWithCases = new Set<Id>();

        for (tr_wc1__KYC_Screening_Case__c cs: [SELECT Id, tr_wc1__Account__c FROM tr_wc1__KYC_Screening_Case__c WHERE CreatedDate > :System.now().addHours(-24) AND tr_wc1__Account__c != null]) {

            accountsWithCases.add(cs.tr_wc1__Account__c);

        }

 

        List<tr_wc1.tr_automateScreening.automateScreeningVars> sCaseVars = new List<tr_wc1.tr_automateScreening.automateScreeningVars>();

        for (Account cont : [SELECT Id FROM Account WHERE LastModifiedDate > :System.now().addHours(-24) AND Id != :accountsWithCases]) {

            tr_wc1.tr_automateScreening.automateScreeningVars item = new tr_wc1.tr_automateScreening.automateScreeningVars();

            item.enableMonitoring = false;

            item.objectType = 'Account';

            item.recordId = acc.Id;

            sCaseVars.add(item);

        }

 

        tr_wc1.tr_BulkCreateAndScreenCases batch = new tr_wc1.tr_BulkCreateAndScreenCases(sCaseVars);

        Database.executeBatch(batch, 30);

    }

}

Example supporting test class:

    	
            

global class AccountScreeningBatchScheduler implements Schedulable {

 

    global void execute(SchedulableContext sc) {

 

        Set<Id> AccountWithCases = new Set<Id>();

        for (tr_wc1__KYC_Screening_Case__c cs: [SELECT Id, tr_wc1__Account__c FROM tr_wc1__KYC_Screening_Case__c WHERE CreatedDate > :System.now().addHours(-24) AND tr_wc1__Account__c != null]) {

            accountsWithCases.add(cs.tr_wc1__Account__c);

        }

 

        List<tr_wc1.tr_automateScreening.automateScreeningVars> sCaseVars = new List<tr_wc1.tr_automateScreening.automateScreeningVars>();

        for (Account cont : [SELECT Id FROM Account WHERE LastModifiedDate > :System.now().addHours(-24) AND Id != :accountsWithCases]) {

            tr_wc1.tr_automateScreening.automateScreeningVars item = new tr_wc1.tr_automateScreening.automateScreeningVars();

            item.enableMonitoring = false;

            item.objectType = 'Account';

            item.recordId = acc.Id;

            sCaseVars.add(item);

        }

 

        tr_wc1.tr_BulkCreateAndScreenCases batch = new tr_wc1.tr_BulkCreateAndScreenCases(sCaseVars);

        Database.executeBatch(batch, 30);

    }

}

Use Case 3: "Skeleton Case Process"

Existing World-Check One Desktop customers who are migrating to Customer Risk Screener may wish to import existing case from World Check One and manage them from within Salesforce going forward.  This can be supported using the "Skeleton Case" process, where Screening Cases are exported from World-Check One Desktop, the Screening Case IDs are then data loaded into the Screening Case Object.  A once off Apex job can then go and fetch the data for the corresponding Screening Case IDs from World-Check.    Click here for more information around the Skeleton case process.

Apex Jobs may be suitable for customers who already have many thousands of records in World-Check One that they wish to manage in Salesforce going forward.

Name: "SkeletonCaseBatchScheduler":

    	
            

global class SkeletonCaseUpdateBatchScheduler implements Schedulable {

 

    global void execute(SchedulableContext sc) {

 

 

        List<tr_wc1.tr_automateScreening.automateScreeningVars> sCaseVars = new List<tr_wc1.tr_automateScreening.automateScreeningVars>();

 

        for (tr_wc1__KYC_Screening_Case__c cs : [SELECT Id, tr_wc1__Case_Id__c, tr_wc1__Thomson_Reuters_Case_Id__c FROM tr_wc1__KYC_Screening_Case__c WHERE LastModifiedDate > :System.now().addHours(-24) AND  tr_wc1__Screening_Complete__c  = false AND tr_wc1__Thomson_Reuters_Group_Id__c = null AND (tr_wc1__Thomson_Reuters_Case_Id__c != null OR tr_wc1__Case_Id__c != null)]) {

            tr_wc1.tr_automateScreening.automateScreeningVars item = new tr_wc1.tr_automateScreening.automateScreeningVars();

            item.enableMonitoring = true;

            item.objectType = 'KYC_Screening_Case';

            item.recordId = cs.Id;

            item.screeningCaseId = cs.Id;

            item.worldCheckOneCaseId = cs.tr_wc1__Case_Id__c;

            item.worldCheckOneCaseSystemId  = cs.tr_wc1__Thomson_Reuters_Case_Id__c;

            sCaseVars.add(item);

        }

 

        tr_wc1.tr_BulkCreateAndScreenCases batch = new tr_wc1.tr_BulkCreateAndScreenCases(sCaseVars);

        Database.executeBatch(batch, 30);

 

    }

}

The above code snippets are examples only.