function readOnly(count){ }
Starting November 20, the site will be set to read-only. On December 4, 2023,
forum discussions will move to the Trailblazer Community.
+ Start a Discussion
Jan AertgeertsJan Aertgeerts 

Trigger to copy object + children to other object at specific moment in approval process

Hello

I was wondering if it is possible to create a copy of the record (with all child-records) to another object when the approval process reaches a certain point.
The case is as following: I have an Invoice object, this invoice has multiple Invoice lines (child). When someone approves the invoice for payment, a copy should be made to the object Invoice_copy (& Invoice_Lines_copy) so that there is a fixed/frozen record of the invoice. This to be certain that it has been booked / paid correctly when approved (so we have proof). 

Is this possible in the way that I describe with a copy to another object, if so - can someone head me in the right direction to create this trigger?

Thanks in advance.

Kind regards
Best Answer chosen by Jan Aertgeerts
BalajiRanganathanBalajiRanganathan
in the sample code I gave,
1) Instead of returning a list, return  a map with key as source Id and value as new object
    instead of this line targetList.add(sObj ); targetMap.put (source.get('Id'), sObj)
2) Remove the insert from the above code
3) call the sample code with parent objects which will return a map.
4) insert the parent copy using insert map.getValues()
5) call the sample code with child object which will return a map
6) iterate the return map.getValues()
7) get the relationship field from the child and get the correspoinding new parent using map and update the relatiohsip field.
8) now insert the child copy map.getValues()

Map<Id, Sobject> parentCopyList = getCreatableFieldsSOQL('parent__c', 'where id in...', 'parent_copy__c');
insert parentCopyList.getValues();

Map<Id, Sobject> childCopyList = getCreatableFieldsSOQL('child__c', 'where id in...', 'child_copy__c');

for (Sobject child : childCopyList ) {
    Sobject newParent = parentCopyList.get (child.get('parent__c'));
    child.put('parent__c', newParent.get('id'))
}

insert childCopyList.getValues();

also note that id field might also get copied in the getCreatableFieldsSOQL method. you need to add if to avoid that
if (field != 'id') {
sObj.put (field, source.get(field));
}

All Answers

BalajiRanganathanBalajiRanganathan
It is a not a good idea to create duplicate data. once the record is approved it will be locked by default. only profiles with modify all permission
on that object will able to edit the record. 
Jan AertgeertsJan Aertgeerts
I understand, but suppose I need the copy for a third party as a verification that even the profiles with modify all permissions can't change. 
BalajiRanganathanBalajiRanganathan
You can use below code to copy the record. I refered the below post and updated it to your needs

http://sfdc.arrowpointe.com/2011/03/28/cloning-records-in-apex/
 
public static List<sObject> getCreatableFieldsSOQL(String objectName, String whereClause, String targetObject){
         
        String selects = '';
         
        if (whereClause == null || whereClause == ''){ return null; }
         
        // Get a map of field name and field token
        Map<String, Schema.SObjectField> fMap = Schema.getGlobalDescribe().get(objectName.toLowerCase()).getDescribe().Fields.getMap();
        list<string> selectFields = new list<string>();
         
        if (fMap != null){
            for (Schema.SObjectField ft : fMap.values()){ // loop through all field tokens (ft)
                Schema.DescribeFieldResult fd = ft.getDescribe(); // describe each field (fd)
                if (fd.isCreateable()){ // field is creatable
                    selectFields.add(fd.getName());
                }
            }
        }
         
        if (!selectFields.isEmpty()){
            for (string s:selectFields){
                selects += s + ',';
            }
            if (selects.endsWith(',')){selects = selects.substring(0,selects.lastIndexOf(','));}
             
        }
         
       List<sObject> sourceList =  Database.query('SELECT ' + selects + ' FROM ' + objectName + ' WHERE ' + whereClause);

    List<sObject> targetList = new List<sObject>();
     for (sObject source : sourceList ) {
      sObject sObj = Schema.getGlobalDescribe().get(ObjectName).newSObject();
      for (String field : selects) {
        sObj.put (field, source.get(field));
      }
      targetList.add(sObj );
     }

     insert targetList;

     return targetList;
    }

Note that you have to work/change  the above code to copy the child object. when you copy the child you have to update the parent relationship field to new parent record.
 
Jan AertgeertsJan Aertgeerts

Hi, many thanks for the code and effort. I can get it to work but am confused how I can iterate through the child records and relate the copies to the new parent object. Can you put me on the right track?

Kind regards
BalajiRanganathanBalajiRanganathan
in the sample code I gave,
1) Instead of returning a list, return  a map with key as source Id and value as new object
    instead of this line targetList.add(sObj ); targetMap.put (source.get('Id'), sObj)
2) Remove the insert from the above code
3) call the sample code with parent objects which will return a map.
4) insert the parent copy using insert map.getValues()
5) call the sample code with child object which will return a map
6) iterate the return map.getValues()
7) get the relationship field from the child and get the correspoinding new parent using map and update the relatiohsip field.
8) now insert the child copy map.getValues()

Map<Id, Sobject> parentCopyList = getCreatableFieldsSOQL('parent__c', 'where id in...', 'parent_copy__c');
insert parentCopyList.getValues();

Map<Id, Sobject> childCopyList = getCreatableFieldsSOQL('child__c', 'where id in...', 'child_copy__c');

for (Sobject child : childCopyList ) {
    Sobject newParent = parentCopyList.get (child.get('parent__c'));
    child.put('parent__c', newParent.get('id'))
}

insert childCopyList.getValues();

also note that id field might also get copied in the getCreatableFieldsSOQL method. you need to add if to avoid that
if (field != 'id') {
sObj.put (field, source.get(field));
}
This was selected as the best answer