0

Scenario: Our company needs to preserve old "Abandoned" cases (due to employee departure, etc) for tracking/metrics purposes, but also need to be able to "Transfer" the cases to a new employee.

To accomplish this, we have a value of "Abandoned" in Case status, a custom picklist field called Abandoned_Reason__c with a value of "Transferred" and a custom lookup relationship called Transfer_To__c which is a lookup to User. When we mark a case as Abandoned/Transferred and set a Transfer To user, before update apex is executed.

The Apex does a .clone() on the oldMap of each record (to get the values prior to being marked Abandoned/Transferred), and updates the Owner on the newly cloned record to match the owner specified in the Transfer_To__c field on the old record.

 

Requirement: I also want to update the Parent Case field on the original case, the one that is now marked as Abandoned. Since I am in the before update context I can manipulate these records still since they are not committed yet.

 

Problem: Since the record(s) being cloned haven't been inserted yet, there are no Ids, so I can't populate the Parent Case field on the old record until after the newly cloned records are inserted. However, there is no link between the newly inserted clone(s) and their original(s), so, while I can loop through the casesToInsert after inserting them to get their Id, I have no way right now to identify which newly cloned and inserted case matches to which originally abandoned case.

 

Thoughts: I've spent a decent amount of time considering different approaches to this. I could create a custom self-lookup on Case where I store the old Case Id, making the new record a child of the old record, and then loop through the newly cloned/inserted records to build a Map<Id,Id> where the Key is the Old_Case_Id__c field, and the value is the Id of the current case.

I would then loop through the Trigger.new context again, and using my map I would identify the key/val pair and update the Parent Case field on the old record to the value in the map.

 

Question: Before I went down this road, I wanted to reach out to the Stack community to see if there is a simpler or more elegant approach to this. Is there a better way that I could clone a record and then make the newly cloned record a parent of its old self, without doing this roundabout circular lookup reference?

   

Method Code below for reference:

public static void cloneTransferCases(Map<Id,Case> oldMap,List<Case> newList){
    System.debug('MM++++ oldMap: ' + oldMap.keySet());
    System.debug('MM++++ oldMap Values: ' + oldMap.values());
    System.debug('MM++++ newList: ' + newList);

    List<Case> casesToInsert = new List<Case>();

    for(Case cs : newList){
        // check to see if this record has been processed already in this transaction
        if(!processedAbandonCloneRecords.contains(cs.Id)){
            if(cs.Status == 'Abandoned'){
                if(cs.Abandoned_Reason__c == 'Transferred' && oldMap.get(cs.Id).Abandoned_Reason__c != 'Transferred'){
                    if(cs.Transfer_To__c != null && cs.ParentId == null){
                        // Create a temporary case using oldMap
                        // to get field values prior to Abandonment
                        Case tempCase = oldMap.get(cs.Id).clone();
                        // set owner of new case to match the Transfer To user
                        tempCase.OwnerId = cs.Transfer_To__c;
                        tempCase.ParentId = null;
                        casesToInsert.add(tempCase);

                        // mark this record as processed in this transaction
                        processedAbandonCloneRecords.add(cs.Id);

                        // if we are running tests, insert and update will
                        // occur in same transaction so we should actually
                        // not stop cascade
                        if(Test.isRunningTest()){
                            processedAbandonCloneRecords.remove(cs.Id);
                        }
                    }
                }
            }
        }
    }

    if(!casesToInsert.isEmpty()){
        insert casesToInsert;
    }
}
1

Using Map instead of List will help us to achieve this functionality. Here is the updated code.

public static void cloneTransferCases(Map<Id,Case> oldMap,List<Case> newList){
    System.debug('MM++++ oldMap: ' + oldMap.keySet());
    System.debug('MM++++ oldMap Values: ' + oldMap.values());
    System.debug('MM++++ newList: ' + newList);
    Set<id> processedAbandonCloneRecords = new Set<Id>();
    Map<Id,Case> mapCaseToInsert = new Map<Id,Case>();

    for(Case cs : newList){
        // check to see if this record has been processed already in this transaction
        if(!processedAbandonCloneRecords.contains(cs.Id)){
            if(cs.Status == 'Abandoned'){
                if(cs.Abandoned_Reason__c == 'Transferred' && oldMap.get(cs.Id).Abandoned_Reason__c != 'Transferred'){
                    if(cs.Transfer_To__c != null && cs.ParentId == null){
                        // Create a temporary case using oldMap
                        // to get field values prior to Abandonment
                        Case tempCase = oldMap.get(cs.Id).clone();
                        // set owner of new case to match the Transfer To user
                        tempCase.OwnerId = cs.Transfer_To__c;
                        tempCase.ParentId = null;
                        mapCaseToInsert.put(cs.id,tempCase);

                        // mark this record as processed in this transaction
                        processedAbandonCloneRecords.add(cs.Id);

                        // if we are running tests, insert and update will
                        // occur in same transaction so we should actually
                        // not stop cascade
                        if(Test.isRunningTest()){
                            processedAbandonCloneRecords.remove(cs.Id);
                        }
                    }
                }
            }
        }
    }

    if(!mapCaseToInsert.isEmpty()){
        insert mapCaseToInsert.values();
    }

    for(Case cs : newList){
        if(mapCaseToInsert.containsKey(cs.id)) {
            cs.ParentId = mapCaseToInsert.get(cs.id).id;
        }
    }
}
  • Ahhh. Awesome. The map values contain the uninserted clone records, we insert them, and then the map value is updated to contain the ID of the inserted records? Hadn't thought of that. Thanks! I'll give it a try now – Morgan Marchese Dec 7 '16 at 17:25

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.