Loading
Important Advisory: iOS Update Impacting CG Mobile ApplicationRead More
Process Compliance Navigator
Create a Report and Apex Classes to Demonstrate Compliance Effectiveness

Create a Report and Apex Classes to Demonstrate Compliance Effectiveness

To provide your compliance officers and control managers with an easy-to-use compliance effectiveness reporting capability, create Apex classes and a joined report.

Required Editions

Available in: Lightning Experience
Available in: All Industry Clouds, Sales Cloud, Service Cloud View editions availability.
User Permissions Needed
To configure Process Compliance Navigator:

Compliance Admin permission set

OR

Modify All Data

Note
Note The Apex classes in this document are examples to show how to create reports to demonstrate compliance at your company. If you create custom objects that are different from the objects listed in this document, you must update the Apex to include your objects.
  1. From Setup, in the Quick Find box, enter Apex Classes, and then select Apex Classes.
  2. Click New.
  3. Enter this Apex Class.
    public with sharing class ComplianceLogsBatchRetriever implements Database.Batchable<ConnectApi.ExplainabilityActionLogDetail> {
        public final String createdAfter;
        public final String createdBefore;
        
        public ComplianceLogsBatchRetriever(String cA, String cB){      
            createdAfter = cA;      
            createdBefore = cB;
        }
        
        // Queries the explainability logs for compliance   
        public Iterable<ConnectApi.ExplainabilityActionLogDetail> start(Database.BatchableContext info) {
            List<ConnectApi.ExplainabilityActionLogDetail> logDetails = new List<ConnectApi.ExplainabilityActionLogDetail>();
            ConnectApi.ExplainabilityActionLogs actionLogs = null;
            String queryMore = null;
            do {
                actionLogs = getActionLogs(queryMore);
                logDetails.addAll(actionLogs.actionLogs);
                queryMore = actionLogs.queryMore;
                System.debug('Query More ************' + queryMore);
            } while (queryMore != null && queryMore != '');
            return logDetails;        
        } 
        
        // Get action logs by calling DES API.
        private ConnectApi.ExplainabilityActionLogs getActionLogs(String queryMore) {
            ConnectApi.ExplainabilityActionLogs actionLogs = ConnectApi.ExplainabilityService.getExplainabilityActionLogs(
                null, // String actionContextCode
                '8', // String applicationType
                'CmplEval', // String applicationSubType
                'CmplBusPrc', // String processType
                createdAfter, // String createdAfter
                createdBefore, // String createdBefore
                null, // String primaryFilter
                null, // String secondaryFilter
                null, // String additionalFilter
                null, // industries.explainability.connect.api.enums.ExplainabilityActionLogSortEnum
                null, // Integer pageSize
                queryMore); // String queryMore
            return actionLogs;
        }
       
       
        // Create the logs in relevant custom compliance log objects.
        public void execute(Database.BatchableContext info, List<ConnectApi.ExplainabilityActionLogDetail> logDetails){
            Map<String, List<String>> transactionIdByLogDetail = getActionLogsByTransactionId(logDetails);
            
            // Create procedure logs and control logs from the map populated above       
            for (String transactionId : transactionIdByLogDetail.keySet()) {
                String procedureId = null;
                String procedureVersionName = null;
                String procedureVersionId = null;
                for (String actionLogJsonString : transactionIdByLogDetail.get(transactionId)) {
                    Map<String, Object> actionLogObject = (Map<String, Object>)JSON.deserializeUntyped(actionLogJsonString);               
                    // For procedure logs, store the procedure info in ValidationProcedure__c 
                    // and business context info in CmplEvaluationLogBusinessContextEvidence__c
                    if (actionLogObject.get('logType').equals('PROCEDURE_LOG')) {
                       ValidationProcedureEvaluationLog__c procedureLog = new ValidationProcedureEvaluationLog__c();
                       procedureId = (String)actionLogObject.get('procedureId');
                       procedureLog.ValidationProcedure__c = procedureId;
                       procedureLog.ValidationProcedureName__c = (String)actionLogObject.get('procedureName');
                       procedureLog.ValidationProcedureApiName__c = (String)actionLogObject.get('procedureApiName');
                       procedureVersionId = (String)actionLogObject.get('procedureVersionId');
                       procedureLog.ValidationProcedureVersionId__c = procedureVersionId;
                       procedureVersionName = (String)actionLogObject.get('procedureVersionName');
                       procedureLog.ValidationProcedureVersionName__c = procedureVersionName;
                       procedureLog.Status__c = (String)actionLogObject.get('status');
                       procedureLog.IsCompliant__c = (Boolean)actionLogObject.get('isCompliant');
                       procedureLog.ErrorId__c = (String)actionLogObject.get('errorId');
                       procedureLog.ErrorMessage__c = (String)actionLogObject.get('errorMessage');
                       procedureLog.BusinessOperationsProcess__c = (String)actionLogObject.get('businessOperationsProcessId');
                       procedureLog.BusinessOperationsProcessName__c = (String)actionLogObject.get('businessOperationsProcessName');
                       procedureLog.BusinessOperationsProcessApiName__c = (String)actionLogObject.get('businessOperationsProcessApiName');
                       procedureLog.TransactionId__c = (String)actionLogObject.get('transactionId');
                       procedureLog.TransactionKey__c = (String)actionLogObject.get('transactionKey');
                       procedureLog.InContextOf__c = (String)actionLogObject.get('inContextOf');
                       String evaluationTimeString = (String)actionLogObject.get('evaluationTime');
                       procedureLog.EvaluationTime__c = (DateTime)JSON.deserialize(evaluationTimeString, datetime.class);
                       procedureLog.LogVersion__c = (String)actionLogObject.get('logVersion');
                       insert procedureLog;
                       for (Object businessContextObj : (List<Object>)actionLogObject.get('businessContext')) {                  
                           Map<String, Object> businessContext = (Map<String, Object>)businessContextObj;
                           CmplEvaluationLogBusinessContextEvidence__c logBizCtxEvidence = new CmplEvaluationLogBusinessContextEvidence__c();                    
                           logBizCtxEvidence.BusinessContextParamName__c = (String)businessContext.get('businessContextEvidenceName');
                           logBizCtxEvidence.BusinessContextParamValue__c = (String)businessContext.get('businessContextEvidenceValue');
                           logBizCtxEvidence.ValidationProcedureEvaluationLog__c = procedureLog.Id;
                           insert logBizCtxEvidence;
                       }
                   } 
                   // For control logs, store control related info in ComplianceControlEvaluationLog__c,
                   // related business context and evidence in CmplEvaluationLogBusinessContextEvidence__c,
                   // related regulations info in CmplEvaluationLogRegulationClauseVersion__c
                   // and related policy info in CmplEvaluationLogPolicyClauseVersion__c
                   else if (actionLogObject.get('logType').equals('CONTROL_LOG')) {
                       ComplianceControlEvaluationLog__c controlLog = new ComplianceControlEvaluationLog__c();                            
                       controlLog.ComplianceControl__c = (String)actionLogObject.get('controlId');
                       controlLog.ComplianceControlName__c = (String)actionLogObject.get('controlName');
                       controlLog.ComplianceControlApiName__c = (String)actionLogObject.get('controlApiName');
                       controlLog.ComplianceControlVersionId__c = (String)actionLogObject.get('controlVersionId');
                       controlLog.ComplianceControlVersionName__c = (String)actionLogObject.get('controlVersionApiName');
                       controlLog.Status__c = (String)actionLogObject.get('status');
                       controlLog.IsCompliant__c = (Boolean)actionLogObject.get('isCompliant');
                       controlLog.ComplianceMessage__c = (String)actionLogObject.get('complianceMessage');
                       controlLog.ErrorId__c = (String)actionLogObject.get('errorId');
                       controlLog.ErrorMessage__c = (String)actionLogObject.get('errorMessage');
                       controlLog.BusinessOperationsProcess__c = (String)actionLogObject.get('businessOperationsProcessId');
                       controlLog.BusinessOperationsProcessName__c = (String)actionLogObject.get('businessOperationsProcessName');
                       controlLog.BusinessOperationsProcessApiName__c = (String)actionLogObject.get('businessOperationsProcessApiName');
                       controlLog.TransactionId__c = (String)actionLogObject.get('transactionId');
                       controlLog.TransactionKey__c = (String)actionLogObject.get('transactionKey');
                       controlLog.InContextOf__c = (String)actionLogObject.get('inContextOf');
                       controlLog.ValidationProcedure__c = procedureId;
                       controlLog.ValidationProcedureVersionId__c = procedureVersionId;
                       controlLog.ValidationProcedureVersionName__c = procedureVersionName;
                       String evaluationTimeString = (String)actionLogObject.get('evaluationTime');
                       controlLog.EvaluationTime__c = (DateTime)JSON.deserialize(evaluationTimeString, datetime.class);
                       controlLog.LogVersion__c = (String)actionLogObject.get('logVersion');
                       insert controlLog;
                       for (Object businessContextAndEvidenceObj : (List<Object>)actionLogObject.get('businessContextAndEvidence')) {                 
                           Map<String, Object> businessContextAndEvidence = (Map<String, Object>)businessContextAndEvidenceObj;
                           CmplEvaluationLogBusinessContextEvidence__c logBizCtxEvidence = new CmplEvaluationLogBusinessContextEvidence__c();                    
                           logBizCtxEvidence.BusinessContextParamName__c = (String)businessContextAndEvidence.get('businessContextEvidenceName');
                           logBizCtxEvidence.BusinessContextParamValue__c = (String)businessContextAndEvidence.get('businessContextEvidenceValue');
                           logBizCtxEvidence.ComplianceControlEvaluationLog__c = controlLog.Id;
                           insert logBizCtxEvidence;                       
                       }                   
                       for (Object regulationClauseVersionId : (List<Object>)actionLogObject.get('regulationClauseVersionIds')) {                    
                           CmplEvaluationLogRegulationClauseVersion__c logRegClVersion = new CmplEvaluationLogRegulationClauseVersion__c();
                           logRegClVersion.ComplianceControlEvaluationLog__c = controlLog.Id;
                           String regClVersionId = (String)regulationClauseVersionId;
                           logRegClVersion.RegulationClauseVersionId__c = regClVersionId;
                           RegulationClauseVersion regClVersion = [SELECT Id, Name FROM RegulationClauseVersion WHERE Id= :regClVersionId];
                           logRegClVersion.RegulationClauseVersionName__c = regClVersion.Name;
                           insert logRegClVersion;                       
                       }
                       for (Object policyClauseVersionId : (List<Object>)actionLogObject.get('policyClauseVersionIds')) {                    
                           CmplEvaluationLogPolicyClauseVersion__c logPolicyVersion = new CmplEvaluationLogPolicyClauseVersion__c();
                           logPolicyVersion.ComplianceControlEvaluationLog__c = controlLog.Id;
                           String policyClVerId = (String)policyClauseVersionId;
                           logPolicyVersion.PolicyClauseVersionId__c = policyClVerId;
                           CompliancePlcyClauseVersion polClClVersion = [SELECT Id, Name FROM CompliancePlcyClauseVersion WHERE Id= :policyClVerId];                       
                           logPolicyVersion.PolicyClauseVersionName__c = polClClVersion.Name;
                           insert logPolicyVersion;
                       }
                   }
               }
           }
        }
        
        // Group Action Logs by transaction id.
        private Map<String, List<String>> getActionLogsByTransactionId(List<ConnectApi.ExplainabilityActionLogDetail> logDetails) {
            Map<String, List<String>> transactionIdByLogDetail = new Map<String, List<String>>();
            for (ConnectApi.ExplainabilityActionLogDetail logDetail : logDetails) {
                String actionLogJsonString = logDetail.actionLog;
                Map<String, Object> actionLogObject = (Map<String, Object>)JSON.deserializeUntyped(actionLogJsonString);                       
    
                // Group logs by transactionId
                String transactionId = (String)actionLogObject.get('transactionId');
                if (transactionIdByLogDetail.containsKey(transactionId)) {
                    transactionIdByLogDetail.get(transactionId).add(actionLogJsonString);
                } else {
                    List<String> logDetailList = new List<String>();
                    logDetailList.add(actionLogJsonString);
                    transactionIdByLogDetail.put(transactionId, logDetailList);
                }
            }
            return transactionIdByLogDetail;
        }
        
       
        public void finish(Database.BatchableContext info) {    
            // Do nothing.
        } 
    }
  4. Similarly, create this Apex class.
    // Compliance Log Retrieval Scheduler. 
    // Run every hour to pull compliance log records from DES.
    // @author: priyanka.bhardwaj
    global with sharing class ComplianceLogRetrievalScheduler implements Schedulable {
    
        private static String DATE_FORMAT = 'yyyy-MM-dd\'T\'HH:mm:ss.SSS\'Z\'';
    
        // This code executes once every hour and calls the ComplianceLogsBatchRetriever to retrieve logs from DES.
        global void execute(Schedulablecontext context) {
            // Set before and after times to retrieve compliance log records.
            // Here we are keeping the time interval as 60 mins.
            String createdAfter = datetime.now().addMinutes(-60).formatGMT(DATE_FORMAT); // 60 mins interval.;
            String createdBefore = datetime.now().formatGMT(DATE_FORMAT);
            
            // Call the Batch Retriever.
            ComplianceLogsBatchRetriever logRetriever = new ComplianceLogsBatchRetriever(createdAfter, createdBefore);
            Database.executeBatch(logRetriever);
        }
    }
  5. From Setup, in the Quick Find box, enter Scheduled Job, and then select Scheduled Job.
  6. Schedule the Apex class to run at a specified time.
    1. Click Scheduled Apex.
    2. Enter a name.
      ComplianceLogsRetrievalJob.
    3. In Apex Class, find and select ComplianceLogRetrievalScheduler.
      If you don't see this Apex class, review the Apex class setup.
    4. In Schedule Using, select Cron Expression.
    5. Enter an expression that runs every hour at a specified minute.
      Expression 03000 runs every hour at 3 minutes past the hour.
    6. Save your changes.
  7. Create a joined report by using the Compliance Control Evaluation Log custom object.
  8. Add blocks:
    • Compliance Control Evaluation Log
    • Evaluation Log Business Context Evidence with Compliance Control Evaluation Log
    • Evaluation Log Regulation Clause Version with Compliance Control Evaluation Log
    • Evaluation Log Policy Clause Versions with Compliance Control Evaluation Log
    Here's a sample report that shows the compliance control logs related to the details in a report.
    <?xml version="1.0" encoding="UTF-8"?>
    <Report xmlns="http://soap.sforce.com/2006/04/metadata" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
        <block>
            <blockInfo>
                <blockId>B0</blockId>
                <joinTable>x</joinTable>
            </blockInfo>
            <columns>
                <field>ComplianceControlEvaluationLog__c.ComplianceControl__c</field>
            </columns>
            <columns>
                <field>ComplianceControlEvaluationLog__c.ComplianceControlApiName__c</field>
            </columns>
            <columns>
                <field>ComplianceControlEvaluationLog__c.ComplianceControlVersionId__c</field>
            </columns>
            <columns>
                <field>ComplianceControlEvaluationLog__c.ComplianceControlVersionName__c</field>
            </columns>
            <columns>
                <field>ComplianceControlEvaluationLog__c.InContextOf__c</field>
            </columns>
            <columns>
                <field>ComplianceControlEvaluationLog__c.Status__c</field>
            </columns>
            <columns>
                <aggregateTypes>Sum</aggregateTypes>
                <field>ComplianceControlEvaluationLog__c.IsCompliant__c</field>
            </columns>
            <columns>
                <field>ComplianceControlEvaluationLog__c.ComplianceMessage__c</field>
            </columns>
            <columns>
                <field>ComplianceControlEvaluationLog__c.ErrorId__c</field>
            </columns>
            <columns>
                <field>ComplianceControlEvaluationLog__c.ErrorMessage__c</field>
            </columns>
            <columns>
                <field>ComplianceControlEvaluationLog__c.EvaluationTime__c</field>
            </columns>
            <columns>
                <field>ComplianceControlEvaluationLog__c.TransactionKey__c</field>
            </columns>
            <columns>
                <field>ComplianceControlEvaluationLog__c.BusinessOperationsProcess__c</field>
            </columns>
            <columns>
                <field>ComplianceControlEvaluationLog__c.BusinessOperationsProcessApiName__c</field>
            </columns>
            <columns>
                <field>ComplianceControlEvaluationLog__c.ValidationProcedure__c</field>
            </columns>
            <columns>
                <field>ComplianceControlEvaluationLog__c.ValidationProcedureVersionId__c</field>
            </columns>
            <columns>
                <field>ComplianceControlEvaluationLog__c.ValidationProcedureVersionName__c</field>
            </columns>
            <format>Summary</format>
            <name>Compliance Control Evaluation Log Info</name>
            <params>
                <name>co</name>
                <value>1</value>
            </params>
            <reportType>CustomEntity$ComplianceControlEvaluationLog__c</reportType>
            <scope>organization</scope>
            <showGrandTotal>true</showGrandTotal>
            <showSubTotals>true</showSubTotals>
            <timeFrameFilter>
                <dateColumn>ComplianceControlEvaluationLog__c.EvaluationTime__c</dateColumn>
                <interval>INTERVAL_CUSTOM</interval>
            </timeFrameFilter>
        </block>
        <block>
            <blockInfo>
                <blockId>B2</blockId>
                <joinTable>fkx</joinTable>
            </blockInfo>
            <columns>
                <field>CmplEvaluationLogBusinessContextEvidence__c.BusinessContextParamName__c</field>
            </columns>
            <columns>
                <field>CmplEvaluationLogBusinessContextEvidence__c.BusinessContextParamValue__c</field>
            </columns>
            <columns>
                <field>CmplEvaluationLogBusinessContextEvidence__c.ValidationProcedureEvaluationLog__c</field>
            </columns>
            <format>Summary</format>
            <name>Control Business Context Info</name>
            <params>
                <name>co</name>
                <value>1</value>
            </params>
            <reportType>CustomEntity$CmplEvaluationLogBusinessContextEvidence__c@CmplEvaluationLogBusinessContextEvidence__c.ComplianceControlEvaluationLog__c</reportType>
            <scope>scopingRule</scope>
            <showGrandTotal>true</showGrandTotal>
            <showSubTotals>true</showSubTotals>
            <timeFrameFilter>
                <dateColumn>CUST_CREATED_DATE</dateColumn>
                <interval>INTERVAL_CUSTOM</interval>
            </timeFrameFilter>
        </block>
        <block>
            <blockInfo>
                <blockId>B1</blockId>
                <joinTable>fkx</joinTable>
            </blockInfo>
            <columns>
                <field>CmplEvaluationLogRegulationClauseVersion__c.RegulationClauseVersionId__c</field>
            </columns>
            <columns>
                <field>CmplEvaluationLogRegulationClauseVersion__c.RegulationClauseVersionName__c</field>
            </columns>
            <format>Summary</format>
            <name>Control Regulation Clause Info</name>
            <params>
                <name>co</name>
                <value>1</value>
            </params>
            <reportType>CustomEntity$CmplEvaluationLogRegulationClauseVersion__c@CmplEvaluationLogRegulationClauseVersion__c.ComplianceControlEvaluationLog__c</reportType>
            <scope>scopingRule</scope>
            <showGrandTotal>true</showGrandTotal>
            <showSubTotals>true</showSubTotals>
            <timeFrameFilter>
                <dateColumn>CUST_CREATED_DATE</dateColumn>
                <interval>INTERVAL_CUSTOM</interval>
            </timeFrameFilter>
        </block>
        <block>
            <blockInfo>
                <blockId>B3</blockId>
                <joinTable>fkx</joinTable>
            </blockInfo>
            <columns>
                <field>CmplEvaluationLogPolicyClauseVersion__c.PolicyClauseVersionId__c</field>
            </columns>
            <columns>
                <field>CmplEvaluationLogPolicyClauseVersion__c.PolicyClauseVersionName__c</field>
            </columns>
            <format>Summary</format>
            <name>Control Policy Clause Info</name>
            <params>
                <name>co</name>
                <value>1</value>
            </params>
            <reportType>CustomEntity$CmplEvaluationLogPolicyClauseVersion__c@CmplEvaluationLogPolicyClauseVersion__c.ComplianceControlEvaluationLog__c</reportType>
            <scope>scopingRule</scope>
            <showGrandTotal>true</showGrandTotal>
            <showSubTotals>true</showSubTotals>
            <timeFrameFilter>
                <dateColumn>CUST_CREATED_DATE</dateColumn>
                <interval>INTERVAL_CUSTOM</interval>
            </timeFrameFilter>
        </block>
        <blockInfo>
            <blockId xsi:nil="true"/>
            <joinTable>x</joinTable>
        </blockInfo>
        <format>MultiBlock</format>
        <groupingsDown>
            <dateGranularity>Day</dateGranularity>
            <field>ComplianceControlEvaluationLog__c.TransactionId__c</field>
            <sortOrder>Asc</sortOrder>
        </groupingsDown>
        <name>Compliance Control Evaluation Logs</name>
        <reportType>CustomEntity$ComplianceControlEvaluationLog__c</reportType>
        <showDetails>true</showDetails>
    </Report>
  9. To demonstrate compliance effectiveness, add fields in each block.
  10. Click Save and Run.

The report is updated based on the expression that you created.

 
Loading
Salesforce Help | Article