We need to keep an audit of all field changes for a Custom Object, Work Experience. I have created a separate child Object, Work Experience Audit, which has all of the same editable fields. I have built an Apex Class that inserts a Work Experience Audit Record based on the Work Experience. For the Work Experience I was able to use a Database.query to return all of the fields and build the Object. However to create the Audit I entered each field and set the corresponding Work Experience value.

How could I optimize this so that it loops through each of the Work Experience Fields and adds the value to the corresponding Audit field?

global without sharing class WorkExperienceAudit_Create {

      label='Create Work Experience Audit' 
      description='Creates Work Experience Audit Record based on supplied Work Experience' 
  global static List<Response> execute(List<Request> requests) {   
      List<Response> responses = new List<Response>();

      for (Request curRequest: requests ){
          Response curResponse = new Response();

          Id workExperience_Id = curRequest.workExperience_Id;

          List<String> wefields = new List<String>(Work_Experience__c.SObjectType.getDescribe().fields.getMap().keySet());
          String soql = ''
                  + ' SELECT ' + String.join(wefields, ',')
                  + ' FROM Work_Experience__c'
                  + ' WHERE Id = \'' + workExperience_Id +'\'';
          Work_Experience__c we = Database.query(soql);

          WorkExperienceAudit__c weAuditRecord = new WorkExperienceAudit__c(
              WorkExperience__c = we.Id,
              Consolidate_Acquisition_EBITDA__c = we.Consolidate_Acquisition_EBITDA__c,
              Consolidated_Acquisition_Revenue__c = we.Consolidated_Acquisition_Revenue__c,
              Date_From__c = we.Date_From__c,
              Date_To__c = we.Date_To__c,
              Employees_Directly_Reporting__c = we.Employees_Directly_Reporting__c,
              End_EBITDA__c = we.End_EBITDA__c,
              End_EV__c = we.End_EV__c,
              End_Market_s__c = we.End_Market_s__c,
              End_Revenue__c = we.End_Revenue__c,
              First_Acquisition_EBITDA__c = we.First_Acquisition_EBITDA__c,
              First_Acquisition_Revenue__c = we.First_Acquisition_Revenue__c,
              Functions_Reporting__c = we.Functions_Reporting__c,
              GrowthTrajectoryContext__c = we.GrowthTrajectoryContext__c,
              Growth_Trajectory__c = we.Growth_Trajectory__c,
              How_was_the_team_different__c = we.How_was_the_team_different__c,
              Opportunity_Source__c = we.Opportunity_Source__c,
              Opportunity_Source_Context__c = we.Opportunity_Source_Context__c,
              Organic_Growth__c = we.Organic_Growth__c,
              Product_s_or_Service_s__c = we.Product_s_or_Service_s__c,
              Reason_for_Joining_Context__c = we.Reason_for_Joining_Context__c
          curResponse.weAuditRecord = weAuditRecord;
  return responses;

  global class InvocableActionException extends Exception{}

  global class Request {
      global Id workExperience_Id;

  global class Response {
      (label='First Work Experience Audit') 
      global WorkExperienceAudit__c weAuditRecord;
  • 1
    have you consider using the out of the box field history tracking feature? or is there a reason why you decided to go with your custom solution?
    – McCubo
    Jun 2, 2023 at 7:10
  • I did consider History Tracking, but the Key Fields we need to track are lLong Text Area. Jun 2, 2023 at 13:04

1 Answer 1


I believe you are trying to create snapshot of work experience custom object records.

you can modify as below:-

  1. First of all get all the fields from targetObject which is instance of Work experience audit sobject instance

    Map<String, Schema.SObjectField> fieldMap = targetObject.getsObjectType().getDescribe().fields.getMap();
  2. Find the list of all the fields from WorkExperience__c using sobject methods. Here sourceObject is instance of WorkExperience__c

    Map<String, Object> sourceFields = sourceObject.getPopulatedFieldsAsMap();
  3. Run a loop through keyset of sourceFields map

  4. Filter out the fields which does not exist on target object and which are not updateable, then assign the value by getting it from source object

I have taken the refrence from here and made changes to it to fit your use case:- Apex: Copy all populated values from one SObject record to another

    Work_Experience__c we = Database.query(soql);
    WorkExperienceAudit__c weAuditRecord = new WorkExperienceAudit__c();
    Map<String, Schema.SObjectField> fieldMap = weAuditRecord.getsObjectType().getDescribe().fields.getMap();
    Map<String, Object> sourceFields = we.getPopulatedFieldsAsMap();
    for (String fieldName : sourceFields.keySet()) {
        Object value = sourceFields.get(fieldName);
        if (fieldMap.containsKey(fieldName)
            && fieldMap.get(fieldName).getDescribe().isUpdateable()
            && (writeBlank || 
                (value != null && 
                    !(value instanceof String && 
                        String.isBlank((String)value))))) {
            weAuditRecord.put(fieldName, sourceFields.get(fieldName));
  • Brilliant! I saw that post, but could not put the pieces together. Thanks for the assist Jun 2, 2023 at 13:05

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .