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
CKRCKR 

Need help for the trigger

Hi,
could someone help me,how to write the trigger based on the following scenario,

for instance, i have a custom field called status (picklist with values 'open' 'Closed') on both Account and opportunity objects,
now if an account has 4 related opportunities with 2 of them with status as 'open' and other 2 as closed,lets say, if an account's status is 'open' along with any of its related opportutnities as 'open', then a custom error has to be thrown if any user try to change the status of the Account from Open to close.
Best Answer chosen by CKR
UC InnovationUC Innovation
If you must write a trigger, the below code should work:
 
trigger NoOppUpdate on Account(before update) 
{
    Set<Id> acctIdSet = new Set<Id>();
    
    for (Account acct : Trigger.new)      
    {
        acctIdSet.Add(acct.Id);              
    } 

    System.debug('acctIdSet: ' + acctIdSet);

    Map<Id, Account> acctMap = new Map<Id, Account>([SELECT Id,Name, Status__c, (SELECT Id FROM Opportunities WHERE Status__c = 'Open') FROM Account WHERE Id in :acctIdSet]);
    
    for (Account acct : Trigger.new)
    {
        List<Opportunity> oppList = acctMap.get(acct.Id).Opportunities;
        
        System.debug('acct.Id: ' + acct.Id);
        System.debug('acct.Status__c: ' + acct.Status__c);
        System.debug('oppList : ' + oppList);
        
        if (acct.Status__c == 'Closed' && oppList != null && oppList.size() >= 1)
        {
           acct.addError('Account ' + acct.Name + ' still has open opportunities.'); 
        }
    }
 }

You should also probably handle the before insert case as well, not just the before update case?

Note that you should also write a trigger on the opportunity as well in case someone changes the opportunity status from Closed to Open and validates the above rule.

Hope that helps!

All Answers

UC InnovationUC Innovation
I think you can accomplish this without code by creating a rollup summary field (like 'Number_Opportunities_Open__c') on the account to count the number of 'open' opportunities of the account.  The filter criteria for the rollup summary on the account's opportunity would be 'Status EQUALS Open'.  Then, your validation rule on the account would be 'IF( AND( ISPICKVAL(Status__c, "Closed"), Number_Opportunities_Open__c >= 1) , true, false)'.

Hope that helps!
CKRCKR
Hi Ken Tang, Thanks for your reply, actually i have a requirement to do this with trigger
This is what my trigger looks like so far,,it is not working as expected yet..
 
trigger NoOppUpdate on Account(before update) 
{  system.debug(' trigger.old1--------------->'+trigger.old);
    if (trigger.IsBefore && trigger.isUpdate)
     { 
        system.debug(' trigger.old2--------------->'+trigger.old);
       set <String> StrAcctIds = New set <String>();
       for (Account Act : Trigger.old)      
           {
              StrAcctIds.Add(Act.Id);              
          } 
          system.debug('StrAcctIds--------------->'+StrAcctIds);
          List <opportunity> OppList = [SELECT AccountId,Name,status__c FROM Opportunity where AccountId in :StrAcctIds ];
          system.Debug('OppList---------->' +OppList);
          String Stt = 'Flase';
          for (Account Act : Trigger.old)
              {
                for (Opportunity Opr : OppList)
                {
                   if (opr.Status__c != 'Closed' && Act.Id == opr.AccountId)
                     { // system.debug('opr.AccountId--------------->'+opr.AccountId); 
                       // Act.addError('You cannot delete an opportunity, Please contact your Salesforce.com Administrator for assistance.');
                        system.debug('opr.AccountId--------------->'+opr.AccountId);
                        system.debug('Act.Id--------------->'+Act.Id);
                        stt = 'true';
                        break; 
                     }
                 }      
              }
         }
     // system.debug('stt.--------------->'+stt.);
      system.debug(' trigger.old3--------------->'+trigger.old);
 }

 
UC InnovationUC Innovation
If you must write a trigger, the below code should work:
 
trigger NoOppUpdate on Account(before update) 
{
    Set<Id> acctIdSet = new Set<Id>();
    
    for (Account acct : Trigger.new)      
    {
        acctIdSet.Add(acct.Id);              
    } 

    System.debug('acctIdSet: ' + acctIdSet);

    Map<Id, Account> acctMap = new Map<Id, Account>([SELECT Id,Name, Status__c, (SELECT Id FROM Opportunities WHERE Status__c = 'Open') FROM Account WHERE Id in :acctIdSet]);
    
    for (Account acct : Trigger.new)
    {
        List<Opportunity> oppList = acctMap.get(acct.Id).Opportunities;
        
        System.debug('acct.Id: ' + acct.Id);
        System.debug('acct.Status__c: ' + acct.Status__c);
        System.debug('oppList : ' + oppList);
        
        if (acct.Status__c == 'Closed' && oppList != null && oppList.size() >= 1)
        {
           acct.addError('Account ' + acct.Name + ' still has open opportunities.'); 
        }
    }
 }

You should also probably handle the before insert case as well, not just the before update case?

Note that you should also write a trigger on the opportunity as well in case someone changes the opportunity status from Closed to Open and validates the above rule.

Hope that helps!
This was selected as the best answer
CKRCKR
Hi Ken Tang,
Thanks for the help it worked,perfect!, even i made some progress on my trigger on that night,the way you have written the trigger is little different in the way that i have written but your trigger looks efficient, below is the trigger that i have bettered, please feel free to provide your inputs on my trigger,hoping that would help me make more sense on the triggers topic.

 
trigger NoOppUpdate on Account(before update) 
{  
    if (trigger.IsBefore && trigger.isUpdate)
     { 
       set <String> StrAcctIds = New set <String>();
       for (Account Act : Trigger.old)      
           {
              StrAcctIds.Add(Act.Id);              
          } 
         
          List <opportunity> OppList = [SELECT AccountId,Name,status__c FROM Opportunity where AccountId in :StrAcctIds ];
          
          String Stt = 'Flase';
          for (Account Act : Trigger.old)
              {
                for (Opportunity Opr : OppList)
                {
                   if (opr.Status__c != 'Closed' && Act.Id == opr.AccountId)
                     { 
                        trigger.new[0].addError('Still they are some related open Opportunities for this Account');
                        
                        stt = 'true';
                        break; 
                     }
                 }      
              }
         }
     
 }
UC InnovationUC Innovation
You don't need the following if statement: "if (trigger.IsBefore && trigger.isUpdate)" since the trigger is defined to only run on "before update".  The other difference is that I queried for the related opportunities for each of the accounts so that I don't need to use two for loops.  Glad to know that you got it working!