---
schema_version: "1"
id: 3e69ea75327de6891c4302abc1a02acab3729752c8a4a6112c87225b144552f2
title: Create a Report and Apex Classes to Demonstrate Compliance Effectiveness
collection: admin
version: current
locale: en-us
portal_url: https://docs.salesforce.com/admin/current/en-us/ind/264-0-0/business_compliance_suite_demonstrate_create_apex.md
canonical_url: https://help.salesforce.com/s/articleView?id=ind.business_compliance_suite_demonstrate_create_apex.htm&release=264.0.0&type=5
source_site: help.salesforce.com
content_type: documentation
content_hash: b97098a2ef3eb95f0a54479b9c666ec5d4f5957cdb262e97286e386675147c92
description: To provide your compliance officers and control managers with an easy-to-use compliance effectiveness reporting capability, create Apex classes and a joined report.
products:
  - Industry Solutions
  - Cross Cloud
release: "264"
---
# 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

<table class="slds-table slds-table_bordered slds-max-medium-table_stacked slds-m-bottom_small edition"><colgroup><col></colgroup><tbody><tr class=""><td class="slds-cell-wrap" style="vertical-align:top;">Available in: Lightning Experience</td></tr><tr class=""><td class="slds-cell-wrap" style="vertical-align:top;">Available in: All Industry Clouds, Sales Cloud, Service Cloud <a href="https://help.salesforce.com/s/articleView?id=ind.business_compliance_suite_editions.htm&amp;release=264.0.0&amp;type=5" title="Review the supported editions and availability for Process Compliance Navigator.">View editions availability</a>.</td></tr></tbody></table>

<table class="slds-table slds-table_bordered slds-max-medium-table_stacked slds-m-bottom_small permission">
        <colgroup>
          <col style="width:50%">
          <col style="width:50%">
        </colgroup>
        <thead class="slds-text-title_caps ">
          <tr class="">
            <th scope="colgroup" class="slds-cell-wrap" colspan="2" style="vertical-align:top;" id="d14865e69">User Permissions
              Needed</th>
          </tr>
        </thead>
        <tbody>
          <tr class="">
            <td data-label="User Permissions Needed" class="slds-cell-wrap" style="vertical-align:top;" headers="d14865e69 ">To configure Process Compliance Navigator:</td>

            <td class="slds-cell-wrap" style="vertical-align:top;" headers="d14865e69 ">
              <p>Compliance Admin permission set</p>
              <p>OR</p>
              <p style="margin-bottom:0;">Modify All
                Data</p>
            </td>

          </tr>
        </tbody>
      </table>

![Note](https://sf-zdocs-cdn-prod.zoominsoftware.com/tdta-ind-business_compliance_suite-264-0-0-production-enus/a9612f4d-d270-413f-ac53-97bccbdc219d/images/icon_note.png)

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](https://help.salesforce.com/s/articleView?id=analytics.reports_joined_create.htm&type=5&language=en_US) 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.