You are here:
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 |
- From Setup, in the Quick Find box, enter Apex Classes, and then select Apex Classes.
- Click New.
-
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. } } -
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); } } - From Setup, in the Quick Find box, enter Scheduled Job, and then select Scheduled Job.
-
Schedule the Apex class to run at a specified time.
- Click Scheduled Apex.
-
Enter a name.
ComplianceLogsRetrievalJob.
-
In Apex Class, find and select
ComplianceLogRetrievalScheduler.
If you don't see this Apex class, review the Apex class setup.
- In Schedule Using, select Cron Expression.
-
Enter an expression that runs every hour at a specified minute.
Expression 03000 runs every hour at 3 minutes past the hour.
- Save your changes.
- Create a joined report by using the Compliance Control Evaluation Log custom object.
-
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> - To demonstrate compliance effectiveness, add fields in each block.
- Click Save and Run.
Did this article solve your issue?
Let us know so we can improve!
