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
mengelmengel 

Need help with Update Trigger / test case

I have a custom object called Branch.
My Account and Opportunity both have a lookup field to this object.
 
I need to ensure that the Opportunity Branch always contains the same value in the Account Branch if the opportunity is edited - I know this is very simple but i'm hitting a mental roadblock.  
 
My trigger below does not appear to be firing when I do an edit and save on the Opportunity record.
 
Also any help with setting up the test case for this would also be helpful.
 
I am new to this and did figure out how to retreive the code using Ant and Eclipse from the sandbox - but I know it will fail on deploy when I try to put it to production without a test case.   I am not particullary savy when it comes to java script.
 
Any input would be appreciated.
trigger OppBranchEqualAccBranch_Update on Opportunity (before update) {
  if (System.Trigger.isUpdate) {
         for(Opportunity oppnew :[select Id, Branch__c, Account.ID, Account.Branch__c from Opportunity where Id in:Trigger.new])
       {
             if(oppnew.Account.Branch__c == null) 
              oppnew.Branch__c = null;
             else                 
              oppnew.Branch__c = oppnew.Account.Branch__c;
              // Update oppnew;
        }       
    }
}
WhyserWhyser
The problem with is with your select statement.
 
Instead of using a select statement to return all the Opportunities that are going to be updated, you should just write
 
Code:
for ( Opportunity oppnew: Trigger.new )

 

If you plan on doing select statements where you return Ids, you would write it like this:
 
Code:
select Id, Branch__c, Account.ID, Account.Branch__c from Opportunity where Id in :Trigger.newMap.keySet()

 
The keySet() will return a Map of Ids, whereas Trigger.new returns an array of opportunity objects.
 
Hope that helps with your trigger.
 


mengel wrote:
I have a custom object called Branch.
My Account and Opportunity both have a lookup field to this object.
 
I need to ensure that the Opportunity Branch always contains the same value in the Account Branch if the opportunity is edited - I know this is very simple but i'm hitting a mental roadblock.  
 
My trigger below does not appear to be firing when I do an edit and save on the Opportunity record.
 
Also any help with setting up the test case for this would also be helpful.
 
I am new to this and did figure out how to retreive the code using Ant and Eclipse from the sandbox - but I know it will fail on deploy when I try to put it to production without a test case.   I am not particullary savy when it comes to java script.
 
Any input would be appreciated.
trigger OppBranchEqualAccBranch_Update on Opportunity (before update) {
  if (System.Trigger.isUpdate) {
         for(Opportunity oppnew :[select Id, Branch__c, Account.ID, Account.Branch__c from Opportunity where Id in:Trigger.new])
       {
             if(oppnew.Account.Branch__c == null) 
              oppnew.Branch__c = null;
             else                 
              oppnew.Branch__c = oppnew.Account.Branch__c;
              // Update oppnew;
        }       
    }
}





Message Edited by Whyser on 11-13-2008 10:27 AM
JonPJonP
Is there a reason you're using a trigger rather than a formula field on Opportunity?  From the Opportunity, I believe you can just display the value of Opportunity.Account.Branch.Name.

Much simpler than keeping two denormalized fields in sync.

mengelmengel
Hi there,
 
Thank you for your quick response.
 
I tried the 1st option 
 
code:
for(Opportunity oppnew :Trigger.new) {
 
the results actually cleared the Opportunity.Branch__c field as though it was not returning a value from the Account.Branch__c to assign.
 
any additional thoughts?
mengelmengel
Can I do that on a lookup field ?
JonPJonP
From what you've said, you already have a lookup field from Account to Branch (say, "Branch__c"), and of course the built-in lookup field from Opportunity to Account. 

What you want to do is add a new formula field of type Text to Opportunity (say, "Related Branch") so the formula looks like this:

 Related Branch (Text) =
 Account.Branch__r.Name 

Now you have a custom field on Opportunity called Related_Branch__c that contains the value of the Opportunity's Account's Branch's Name field, and it's always in sync.

It all comes down to how you want to use this field.
  • If you need the Branch ID when you are querying Opportunities in SOQL (from Apex or an integration) you don't need a formula field at all--you can just included "Account.Branch__c" (or the equivalent "Account.Branch__r.Id") in your SELECT statement and it will pull down that information in your query.
  • If you want to display fields from the Branch on the Opportunity page layout, then you'll need to create the explicit formula field(s) on Opportunity referencing Branch fields across the Account relationship.
If you go the formula field path, use the "Insert Field" button to easily find the field you're looking for and insert it into the formula text area.

WhyserWhyser
The only reason I can think of is because Opportunity.Account.Branch__c is returning null?
 
There is another way to do it, by using workflow rules and field updates.
 
Set up a workflow rule for the opportunity with a formula that basically says:
 
Branch__c != Account.Branch__c
 
When this evaluates to true (meaning that the Opportunity Branch and the Account Branch are not the same), have it run a Field Update
Associate the Field Update to the Opportunity.Branch__c field, and use the following formula as it's value:
 
Account.Branch__c
 
This is much easier than writing apex code.

mengel wrote:
Hi there,
 
Thank you for your quick response.
 
I tried the 1st option 
 
code:
for(Opportunity oppnew :Trigger.new) {
 
the results actually cleared the Opportunity.Branch__c field as though it was not returning a value from the Account.Branch__c to assign.
 
any additional thoughts?



WhyserWhyser
Sorry mengel, my bad.
 
I was looking at what I wrote yesterday and I realized I did something wrong as well.
 
When you simply get all the opportunities that are to be updated by saying for (Opportunity oppnew :Trigger.new), there's no point in which it returns any Account information related to the opportunity, that's why you're always returning a null value, even if the Account value is set.
 
The only way would be to run the SOQL statement in which I provided for you earlier, because those results specifically return the information requested in your query, which includes the Account.Branch__c values.
 
However, I think the standard workflow rules and field updates are a better way to go to accomplish what you're trying to do.
 

Whyser wrote:
The only reason I can think of is because Opportunity.Account.Branch__c is returning null?
 
There is another way to do it, by using workflow rules and field updates.
 
Set up a workflow rule for the opportunity with a formula that basically says:
 
Branch__c != Account.Branch__c
 
When this evaluates to true (meaning that the Opportunity Branch and the Account Branch are not the same), have it run a Field Update
Associate the Field Update to the Opportunity.Branch__c field, and use the following formula as it's value:
 
Account.Branch__c
 
This is much easier than writing apex code.

mengel wrote:
Hi there,
 
Thank you for your quick response.
 
I tried the 1st option 
 
code:
for(Opportunity oppnew :Trigger.new) {
 
the results actually cleared the Opportunity.Branch__c field as though it was not returning a value from the Account.Branch__c to assign.
 
any additional thoughts?






mengelmengel

Hi Whyser,

I like the idea of using the workflow rule however, it Looks like I can't use a workflow rule to update a lookup field. it's not available for update in the drop-down list.

I also toyed with Jon's suggestion of changing the field to a formula 

that however will entail additional changes to s-controls etc. we already have customized.

Sooo I think my only option is the trigger but still having issues getting the update to occur without the recursive update.

Thanks for the suggestions!

 

 

WhyserWhyser

You're probably going to have to go through your trigger step by step then:

- Use Apex Explorer or Eclipse to ensure that the query you're attempting to run returns the correct data (replace your Trigger.newMap.keySet() with an actual opportunity id or multiple Ids)
- Write some System.Debug(' '); statements in places around your trigger to ensure you're getting the right data.

 

Code:
eg. 
System.Debug( 'Trigger.newMap.keySet(): ' + Trigger.newMap.keySet() + '\n\n' ); System.Debug( 'SQL Results:\n' + [select Id, Branch__c, Account.ID, Account.Branch__c from Opportunity where Id in :Trigger.newMap.keySet()] + '\n\n' ); System.Debug( 'oppnew: ' + oppnew + '\n\'n' ); System.Debug( 'oppnew: ' + oppnew.Account.Branch__c + '\n\'n' );

- Write your testmethod code that will run this trigger, and then either in Salesforce or Eclipse, run the tests and view your debug log for your System.Debug statements.

I hope that helps. The debug statements actually helped me figure out what's wrong with my code for the first time I wrote Apex triggers.


Message Edited by Whyser on 11-17-2008 08:19 AM
JonPJonP
Mengel,

I must admit I'm skeptical about your statement that your existing s-controls would be impacted by using a formula field rather than a trigger.  It sounds like you have a lookup field right now, but it doesn't work, so none of your s-controls that use that field would work either.  If your s-controls need an Id value rather than a Name, create a lookup to the Id instead.  If you need both the branch Id and Name (e.g. for s-controls and for page layouts) then use both.

If your s-controls are setting the Branch__c lookup field, doesn't that interfere with your trigger logic anyway?

Could you say more about the s-control/other constraints that are driving you to use a trigger (which based on my current understanding sounds very much like the wrong long term solution)?

Thanks,
Jon
mengelmengel
Jon Wrote
  "  Mengel,

I must admit I'm skeptical about your statement that your existing s-controls would be impacted by using a formula field rather than a trigger.  It sounds like you have a lookup field right now, but it doesn't work, so none of your s-controls that use that field would work either.  If your s-controls need an Id value rather than a Name, create a lookup to the Id instead.  If you need both the branch Id and Name (e.g. for s-controls and for page layouts) then use both.

If your s-controls are setting the Branch__c lookup field, doesn't that interfere with your trigger logic anyway?

Could you say more about the s-control/other constraints that are driving you to use a trigger (which based on my current understanding sounds very much like the wrong long term solution)?
"
 
Hi Jon,
     
The lookup field for Branch does work - as do the current s-controls...  I will however be removing the update in the S-control for this field as soon as I get my trigger issues resolved.
 
I am attempting to force the trigger to always override the Branch Id  to be the same as the Branch Id from the associated Account record -  this field will then be changed to read-only on the Opportunity page layout.
 
I agree that the best solution would be to use a formula rather than the lookup field. - however after looking at our reports that have already been setup over the "Opportunity And Branch"  relationship and the need to run Opportunity History which does not allow selections from teh Account record for report sorting on the Branch field.   I will need to go with the trigger.
 
I have successfully created a trigger on the Account which will review all Opportunities associated to it and update the Branch lookup field correctly if the Accounts branch is changed.
 
I also have a trigger on the Opportunity which will allow me to set the branch when the Opportunity is created.

But I am still having an issue with the Opportunity Update.  I am not real handy with apex code and have been struggling to find just some simple samples.  I think what i'm doing is more complicated then it needs to be.

thanks,  Jackie

my last attempt resulted in this error:

Error: Invalid Data.
Review all error messages below to correct your data.
Apex trigger OppBranchEqualAccBranch_Update caused an unexpected exception, contact your administrator: OppBranchEqualAccBranch_Update: execution of BeforeUpdate caused by: System.DmlException: Update failed. First exception on row 0 with id 006T0000003uCfZIAU; first error: SELF_REFERENCE_FROM_TRIGGER, Object (id = 006T0000003uCfZ) is currently being updated in trigger OppBranchEqualAccBranch_Update: [Id]: Trigger.OppBranchEqualAccBranch_Update: line 34, column 5

trigger OppBranchEqualAccBranch_Update on Opportunity (before update) {
    // The map allows us to track changed opportunities  
    Map<Id, Opportunity > updOpps = new Map<Id, Opportunity>();
    // Trigger.new is a list of the Opportunities that will be updated
    // This loop iterates over the list, and adds any that have updates
  
    for (Integer i = 0; i < Trigger.new.size(); i++) {
       
            updOpps.put(Trigger.old[i].id, Trigger.new[i]);
     }
    List<Opportunity> updatedOpportunities = new List<Opportunity>();
    //Here we can see two syntatic features of Apex:
    // 1) iterating over an embedded SOQL query
    // 2) binding an array directly to a SOQL query with 'in'
    for (Opportunity c : [SELECT Id, AccountId, Branch__c, Account.Branch__c
                      FROM opportunity WHERE Id in :updOpps.keySet()]) {
        c.Branch__c = c.Account.Branch__c;
               
        // Rather than insert the contacts individually, add the
        // contacts to a list and bulk insert it. This makes the
        // trigger run faster and allows us to avoid hitting the
        // governor limit on DML statements
        updatedOpportunities.add(c);
    }      
    update updatedOpportunities;
}
WhyserWhyser
If I were to write the code, I think i would write something like this
 
Code:
trigger OppBranchEqualAccBranch_Update on Opportunity (before update)
{
 // Iterate through all opportunities being updated
 for ( Opportunity o: Trigger.New )
 {
// Obtain the Branch__c from the Account Account a = [select Branch__c From Account where Id = :o.AccountId];
// Write the Opportunity.Branch__c with the value from the Account.Branch__c o.Branch__c = a.Branch__c; }
// You're done, you don't need to call an update command since the Trigger.New values will be saved. }

 
All the values in Trigger.New will be used when saving the record, so you don't need to call an update command.
 
I didn't check the code, so I don't know if this will work, but I'm pretty confident that this is how you would approach this.


Message Edited by Whyser on 11-18-2008 01:08 PM
mengelmengel
Thank You Whyser - That worked !
 
Thanks for the help !
 
Jackie