I am trying to prevent trigger from creating duplicate requests.

For example, if the case status is changed from something else to close, then create a new request. If the case status is not changed from something else to closed but it was already closed and edit button was clicked and without doing any changes, it was saved, in this case it should not create a new request.

I have trigger and handler class all set up for this but, i don't know why old values are being passed in both of my maps in handler class even though inside the trigger, it is fetching new and old values. still in my map inside handler class, it is passing old values where status is New. I checked the log.

Below is what i have tried

public class MaintenanceRequestHelper {

    public static Boolean isFirstTime = true;

    public static void updateWorkOrders(Map<Id,Case> newVal, Map<Id,Case> oldVal) {
        Map<Id,Case> maintenance_Req_List = new Map<Id,Case>([SELECT id, Origin, Status, Date_Due__c, priority, 
 Case.vehicle__c, Equipment__c, Equipment__r.Maintenance_Cycle__c, Type, Subject, Date_Reported__c FROM Case Where Status in:newVal.KeySet()]);
        Map<Id,Case> maintenance_Req_Listt = new Map<Id,Case>([SELECT id, Origin, Status, Date_Due__c, priority, 
 Case.vehicle__c, Equipment__c, Equipment__r.Maintenance_Cycle__c, Type, Subject, Date_Reported__c FROM Case Where id in:oldVal.KeySet()]);

    public static void closedMaintenanceRequestList(Map<Id, Case> newMap,  Map<Id, Case> oldMap) {
        List<Case> new_Maintenance_Req_Lst = new List<Case>();
        for (Case c: newMap.Values()) {
            if(c.Type == 'Repair' || c.Type == 'Routine Maintenance'){
                System.debug('routine if is true');
                if (c.Status == 'Closed' && oldMap.get(c.Id).Status != 'Closed' ) {
                    System.debug('if executed after being true');

                    Case new_req = new Case();
                    new_req.Subject = 'Maintenance request';
                    new_req.Type = 'Routine Maintenance';
                    new_req.Vehicle__c = c.Vehicle__c;
                    new_req.Equipment__c = c.Equipment__c;
                    new_req.Date_Reported__c = Date.today();
                    new_req.Date_Due__c = Date.today().addDays(Integer.valueOf(c.Equipment__r.Maintenance_Cycle__c));
                    new_req.Status = 'New';
                    new_req.Origin = 'Phone';

        if(new_Maintenance_Req_Lst.size() > 0){
            insert new_Maintenance_Req_Lst;

and trigger

trigger MaintenanceRequest on Case (before update,after update) {
        MaintenanceRequestHelper.isFirstTime = false;
        System.debug('Trigegr fired');
        MaintenanceRequestHelper.updateWorkOrders(Trigger.newMap, Trigger.oldMap);          

Here are the values from log. For new values:

SSIGNMENT [5]|newVal|{"5002o00002Br9G9AAJ":{"Origin":"Phone","Status":"Closed","LastModifiedDate":"2019-08-05T12:29:24.000Z","IsDeleted":false,"Date_Due__c":"2019-08-09T00:00:00.000Z","Priority":"Medium","Equipment__c":"01t2o000007zDQfAAM","IsClosed":false,"SystemModstamp":"2019-08-05T12:29:24.000Z","BusinessHoursId":"01m2o000000U4OdAA

For old values:

IGNMENT [5]|oldVal|{"5002o00002Br9G9AAJ":{"Origin":"Phone","Status":"New","LastModifiedDate":"2019-08-05T12:29:24.000Z","IsDeleted":false,"Date_Due__c":"2019-08-09T00:00:00.000Z","Priority":"Medium","Equipment__c":"01t2o000007zDQfAAM","IsClosed":false,"SystemModstamp":"2019-08-05T12:29:24.000Z","BusinessHoursId":"01m2o000000U4OdAAK&q

But in my map inside handler class. both values are where status is New.

Please help. I am totally lost here.

  • Welcome to SFSE! I've taken the liberty of formatting your question and your code so that they are easier to read. Being consistent with your indentation, spacing, and bracket style really do help other people read your code. Of those, I'd say that being consistent with indentation is the most important because it makes the difference between the inside and outside of things like methods, if blocks, and for loops really easy to spot. The easier you make it for people to help you, the more (and more accurate) answers you'll tend to get.
    – Derek F
    Aug 5, 2019 at 13:19
  • Hello @DerekF, I will be careful about that next time.
    – Deviker
    Aug 5, 2019 at 13:49

2 Answers 2


In your trigger handler, you are re-querying the Case records into new maps, and passing those maps to the closedMaintenanceRequestList() method. In the before trigger context, the values in Trigger.newMap have not yet been saved to the database.

So when you query those records from the database again, you are getting their values from before the record edit that fired the trigger. I don't know whether the updateWorkOrders() method might provide value elsewhere in your application - it seems like it could likely be removed entirely, but I can't say for sure.

Calling MaintenanceRequestHelper.closedMaintenanceRequestList(Trigger.newMap, Trigger.oldMap); from your trigger instead of updateWorkOrders() should give you the results you're looking for.

To get the Equipment__r.Maintenance_Cycle__c field, I would either put a formula field on Case for it, or, rather than re-querying the cases, loop over them, building a Set<Id> of Equipment__c records, and then query a Map<Id,Equipment__c> with the Maintenance_Cycle__c field:

Set<Id> equipIds = new Set<Id>();
for (Case c: newVal.values()) {

Map<Id,Equipment__c> equipMap = new Map<Id,Equipment__c>([SELECT Id, Maintenance_Cycle__c FROM Equipment__c WHERE Id IN :equipIds]);  

Then assign the due date from that Map:

new_req.Date_Due__c = Date.today().addDays(Integer.valueOf(equipMap.get(c.Equipment__c).Maintenance_Cycle__c)); 
  • 1
    This answer is close to wall-o-text territory for me. I'd suggest breaking it up a bit to make it easier to digest.
    – Derek F
    Aug 5, 2019 at 13:22
  • Thomas Taylor, Thank you for your answer. I actually need those queries because I need to fetch data of related objects.
    – Deviker
    Aug 5, 2019 at 13:52
  • Updated answer to address that. Aug 5, 2019 at 15:06

When you update your code from New to Close, This is the order of execution.

  1. Before Trigger runs
  2. It calls updateWorkOrders and it creates new Maintainence Request Case
  3. Afer Trigger Runs
  4. It calls updateWorkOrders again and it creates new Maintainence Request Case again

So, before and update triggers both are running and both have same logic.

When we have to upsert related object we should only use after update.

So if you alter your trigger to just after update your code wont create duplicates.

trigger MaintenanceRequest on Case (after update) {

    MaintenanceRequestHelper.updateWorkOrders(Trigger.newMap, Trigger.oldMap);

  • This only covers part of the issue (the other part was identified by Thomas Taylor). Both parts are really needed to resolve the issue here.
    – Derek F
    Aug 5, 2019 at 13:24
  • @Pranay Jaiswal, thank you for that, it did solve my problem to some extent.
    – Deviker
    Aug 5, 2019 at 13:50

You must log in to answer this question.

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