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
Samuel Gonsalves 8Samuel Gonsalves 8 

Trigger getting fired twice because of update

Hi Team,

I have 2 objects quote and invoice. Whenever quote is created and when it is updated to Approved , all data goes to invoice object and a invoice record get created.
My problem is when anytime other fields in quote is updated again the trigger runs and a new invoice gets created. i just want my trigger to run only once. This is my code.

trigger createInvoiceQuote on Quote (after update)
{
   
   
    if(trigger.isAfter){
    set<id> oppIds = new set<id>();
    list<Invoicing__c> newInvoiceList = new list<Invoicing__c>();
    list<Quote> QuoteList = new list<Quote>();
    map<id,id> conRoleMap = new map<id, id>();
    set<id> accSet = new set<id>();
    map<id, contact> accConMap = new map<id , contact>();

    QuoteList = [select id, status, name, isSyncing, opportunityId, contact.Billing_Contact__c , contactId, opportunity.Fieldwork_Stage__c, AccountId, Account.GST_Setting__c from quote where id IN: trigger.newMap.keyset()];
    
    system.debug('QuoteList' + QuoteList);
    
    for(Quote quo : QuoteList){
        if(quo.isSyncing && quo.Status.equalsIgnoreCase('Approved')){
                oppIds.add(quo.opportunityId);
                accSet.add(quo.AccountId);
        }
    }
    
    for(OpportunityContactRole conRole : [select id, contactId, IsPrimary, opportunityId from OpportunityContactRole where opportunityId in: oppIds AND isPrimary = true])
    {
        conRoleMap.put(conRole.opportunityId , conRole.contactId);
    }
    
    for(contact con : [select id, accountId, email, billing_Contact__c from contact where id in: conRoleMap.values() OR (accountId in: accSet AND Billing_Contact__c = true)])
    {
        accConMap.put(con.AccountId , con);
        accConMap.put(con.id , con);
    }
    
    system.debug('conRoleMap ' + conRoleMap);

    for(Quote quo : QuoteList){
        if(quo.isSyncing && quo.Status.equalsIgnoreCase('Approved')){               
            Invoicing__c inv = new Invoicing__c();
            inv.Quote__c = quo.Id;
            inv.Invoice_Name__c = quo.Name;
            inv.Status__c = quo.opportunity.Fieldwork_Stage__c;
            inv.Opportunity_Name__c = quo.OpportunityId;
            inv.GST_Setting__c = quo.Account.GST_Setting__c;
        if(accConMap.containsKey(quo.AccountId)){
            Contact secCon = accConMap.get(quo.AccountId);
            if(secCon.Billing_Contact__c){
                inv.Secondary_Billing_Contact__c = secCon.Id;
                inv.Secondary_Billing_Contact_Email_id__c = secCon.email;
            }
        }
        if(conRoleMap.containsKey(quo.OpportunityId)){
            inv.Primary_Billing_Contact__c = conRoleMap.get(quo.OpportunityId);
            if(accConMap.containsKey(conRoleMap.get(quo.OpportunityId)))
                inv.Primary_Billing_Contact_Email_id__c = accConMap.get(conRoleMap.get(quo.OpportunityId)).email;
        }
            newInvoiceList.add(inv);
        }
    }
        
        for(Invoicing__c inv : [select id, Opportunity_Name__c from Invoicing__c where Opportunity_Name__c in: oppIds]){
            inv.Opportunity_Name__c = null;
            newInvoiceList.add(inv);
        }
        
        upsert newInvoiceList;
    }

}

Can anyone please help. Need it urgently

Thanks.

Regards,
Samuel
 
KaranrajKaranraj
Samuel - The logic you are going to add is old and new value of the record status should not equal in the if condition, so it will fire only once. If there is any change in the statuc then it will fire again
Trigger.oldMap.get(quo.Id).Status != quo.Status
Try the below code
trigger createInvoiceQuote on Quote (after update)
{
   
   
    if(trigger.isAfter){
    set<id> oppIds = new set<id>();
    list<Invoicing__c> newInvoiceList = new list<Invoicing__c>();
    list<Quote> QuoteList = new list<Quote>();
    map<id,id> conRoleMap = new map<id, id>();
    set<id> accSet = new set<id>();
    map<id, contact> accConMap = new map<id , contact>();

    QuoteList = [select id, status, name, isSyncing, opportunityId, contact.Billing_Contact__c , contactId, opportunity.Fieldwork_Stage__c, AccountId, Account.GST_Setting__c from quote where id IN: trigger.newMap.keyset()];
    
    system.debug('QuoteList' + QuoteList);
    
    for(Quote quo : QuoteList){
    if(quo.isSyncing && quo.Status.equalsIgnoreCase('Approved') && Trigger.oldMap.get(quo.Id).Status != quo.Status)       {
                oppIds.add(quo.opportunityId);
                accSet.add(quo.AccountId);
        }
    }
    
    for(OpportunityContactRole conRole : [select id, contactId, IsPrimary, opportunityId from OpportunityContactRole where opportunityId in: oppIds AND isPrimary = true])
    {
        conRoleMap.put(conRole.opportunityId , conRole.contactId);
    }
    
    for(contact con : [select id, accountId, email, billing_Contact__c from contact where id in: conRoleMap.values() OR (accountId in: accSet AND Billing_Contact__c = true)])
    {
        accConMap.put(con.AccountId , con);
        accConMap.put(con.id , con);
    }
    
    system.debug('conRoleMap ' + conRoleMap);

    for(Quote quo : QuoteList){
   if(quo.isSyncing && quo.Status.equalsIgnoreCase('Approved') && Trigger.oldMap.get(quo.Id).Status != quo.Status)
{               
            Invoicing__c inv = new Invoicing__c();
            inv.Quote__c = quo.Id;
            inv.Invoice_Name__c = quo.Name;
            inv.Status__c = quo.opportunity.Fieldwork_Stage__c;
            inv.Opportunity_Name__c = quo.OpportunityId;
            inv.GST_Setting__c = quo.Account.GST_Setting__c;
        if(accConMap.containsKey(quo.AccountId)){
            Contact secCon = accConMap.get(quo.AccountId);
            if(secCon.Billing_Contact__c){
                inv.Secondary_Billing_Contact__c = secCon.Id;
                inv.Secondary_Billing_Contact_Email_id__c = secCon.email;
            }
        }
        if(conRoleMap.containsKey(quo.OpportunityId)){
            inv.Primary_Billing_Contact__c = conRoleMap.get(quo.OpportunityId);
            if(accConMap.containsKey(conRoleMap.get(quo.OpportunityId)))
                inv.Primary_Billing_Contact_Email_id__c = accConMap.get(conRoleMap.get(quo.OpportunityId)).email;
        }
            newInvoiceList.add(inv);
        }
    }
        
        for(Invoicing__c inv : [select id, Opportunity_Name__c from Invoicing__c where Opportunity_Name__c in: oppIds]){
            inv.Opportunity_Name__c = null;
            newInvoiceList.add(inv);
        }
        
        upsert newInvoiceList;
    }

}

 
Samuel Gonsalves 8Samuel Gonsalves 8
Hi Karanraj,

Thanks for a quick response. Its not related to old and new status. Its relating to any field on quote object. The first time when i sync and Approved the quote the invoice should get created. Later after some time if any goes into the same quote and edit some other field values then again a new invoice gets created. I dont want a new invoice to be created . The trigger should run only once.
KaranrajKaranraj
Yes the condition will execute your code only if there is change in the status field. It will simply igoner if there is change in the some other fields.
Stanley JacobsStanley Jacobs
Hi, 

Understand that your trigger is being executed again when user update quotation record, which is not as expected.

My suggestion:
- Create a new field "Is_Invoice_Generated__c" checkbox on quotation level. Set default value unchecked.
- Create after insert trigger on Invoice. Update Quote.Is_Invoice_Generated__c to true.
- Modify your code:
for(Quote quo : QuoteList){
//add condition
        if(!quo.Is_Invoice_Generated__c && quo.isSyncing && quo.Status.equalsIgnoreCase('Approved')){               
            Invoicing__c inv = new Invoicing__c();
            inv.Quote__c = quo.Id;
            inv.Invoice_Name__c = quo.Name;
            inv.Status__c = quo.opportunity.Fieldwork_Stage__c;
            inv.Opportunity_Name__c = quo.OpportunityId;
            inv.GST_Setting__c = quo.Account.GST_Setting__c;
        if(accConMap.containsKey(quo.AccountId)){
            Contact secCon = accConMap.get(quo.AccountId);
            if(secCon.Billing_Contact__c){
                inv.Secondary_Billing_Contact__c = secCon.Id;
                inv.Secondary_Billing_Contact_Email_id__c = secCon.email;
            }
        }
        if(conRoleMap.containsKey(quo.OpportunityId)){
            inv.Primary_Billing_Contact__c = conRoleMap.get(quo.OpportunityId);
            if(accConMap.containsKey(conRoleMap.get(quo.OpportunityId)))
                inv.Primary_Billing_Contact_Email_id__c = accConMap.get(conRoleMap.get(quo.OpportunityId)).email;
        }
            newInvoiceList.add(inv);
        }
    }

 
Samuel Gonsalves 8Samuel Gonsalves 8
Thanks everyone it was really helpful. i have done this

 for(Quote test : trigger.new){           accountmap.put(test.id,test.name);           string name1= System.Trigger.oldMap.get(test.Id).name;           system.debug('name1----------------'+name1);//old value            system.debug('accountmap-==========='+accountmap);//new value            if((test.isSyncing && !trigger.oldMap.get(test.id).isSyncing) || (test.Status.equalsIgnoreCase('Approved') && !trigger.oldmap.get(test.Id).Status.equalsIgnoreCase('Approved')))              QuoteIds.add(test.Id);      }