You need to sign in to do that
Don't have an account?
Before Insert Trigger question
I am writting a before insert trigger to copy information from the related Account record to an opporunity record on insert. I'm creating the test account records, and using them in the opportunities that my test creates. When I look at the Opportunities records as I am creating them, the Account fields (name and ID) are filled in correctly on the opportunities. When my trigger tries to build a set of accounts to copy from, the Account field(s) do not show up in the Trigger.new copies of the records, although all the other fields do show up. I'll attach the trigger and the test code below. Can someone help me understand what is happening to my Account fields? Do I need to store the Account IDs in separate text fields to be able to get the accounts back?
Here's the code for the trigger:
trigger FillFieldsFromAcct on Opportunity (before insert){ { RecordType rt1 = [select id from RecordType where Name = 'PubDev Opportunities' and SobjectType = 'Opportunity' limit 1]; Set<String> aNameSet1 = new Set<String>(); List<Opportunity> oppsToInsert = new List<Opportunity>(); List<Account> acctsToInsert = new List<Account>(); Map<string,string> MasterMap= new Map<string,string>(); for(Opportunity o : Trigger.New){ o = (Opportunity)o; aNameSet1.add(o.Account.Name); MasterMap.put(o.name, o.account.name); } string qry = 'Select Name,Mobile__c, Video__c, Display__c, Monthly_Display_Imps__c, Monthly_Mobile_Imps__c, Monthly_Video_Imps__c,' + ' uniques__c, vertical__c, Pageviews__c from Account where Name IN :aNameSet1'; string qry2 = 'Select Name,Mobile__c, Video__c, Display__c, Monthly_Display_Impressions__c, Monthly_Mobile_Impressions__c, Monthly_Video_Impressions__c,' + ' uniques__c, vertical__c, Pageviews__c, Account.id, Account.name from Opportunity where Account.name IN :aNameSet1'; Map<string, Account>aNameToAccountMap = new Map<Id,Account>((List<Account>)Database.query(qry)); Map<string, Opportunity>OppMap= new Map<string, Opportunity>((List<Opportunity>)Database.query(qry2)); For(Opportunity thisopp : Trigger.new){ thisopp = (Opportunity)thisopp; Account acctToCopy = new Account(); if(thisopp.RecordTypeId != rt1.id){ system.debug('thisopp.RecordTypeId = ' + thisopp.RecordTypeID ); return; } string myAcct = thisopp.Account.name; acctToCopy = aNameToAccountMap.get(myAcct); if(acctToCopy == NULL){ return; } acctToCopy = (Account)acctToCopy; system.debug('acctToCopy = '+ acctToCopy.name); thisopp.Display__c = acctToCopy.Display__c; thisopp.Mobile__c = acctToCopy.Mobile__c; thisopp.Video_PubDev__c = acctToCopy.Video__c; thisopp.Monthly_Display_Impressions__c = acctToCopy.Monthly_Display_Imps__c; thisopp.Monthly_Video_Impressions__c = acctToCopy.Monthly_Video_Imps__c; thisopp.Monthly_Mobile_Impressions__c = acctToCopy.Monthly_Mobile_Imps__c; thisopp.Uniques__c = acctToCopy.Uniques__c; thisopp.Pageviews__c = acctToCopy.Uniques__c; thisopp.Vertical__c = acctToCopy.Vertical__c; system.debug('thisopp has been created -- back to Test ' + thisopp); } } }
And here is the test:
@isTest
private class TestingOppTrigger {
static testMethod void verify_Opp_Fillin() {
List<Opportunity> oppsToInsert = new List<Opportunity>();
List<Account> acctsToInsert = new List<Account>();
RecordType rt = [select id from RecordType where Name = 'PubDev Opportunities' and SobjectType = 'Opportunity' limit 1];
integer y = 0;
Account s = new Account();
Opportunity o = new Opportunity();
//create Accounts
Account testAcct1 = new Account(Name='KBBTestAcct1', Vertical__c = 'Education',Uniques__c = 30000, Pageviews__c=20000,Display__c=TRUE, Video__c =TRUE,
Mobile__c = TRUE, Monthly_Display_Imps__c = 500000,Monthly_Mobile_Imps__c = 500003, Monthly_Video_Imps__c = 500002);
acctsToInsert.add(TestAcct1);
Account testAcct2 = new Account(Name='KBBTestAcct2');
acctsToInsert.add(TestAcct2);
try{
database.insert(acctsToInsert);
} catch(DMLException e) {
system.debug('ERROR: ' + e);
}
//Map inserted Accounts
string qry3 = 'select name, id, display__c, mobile__c, video__c, Monthly_Display_Imps__c, Monthly_video_Imps__c,' +
' Monthly_Mobile_Imps__c, Uniques__c, Pageviews__c, Vertical__c ' +
' from Account where CreatedDate = TODAY';
Map<string, Account> AccountMap = new Map<string, Account>((List<account>)Database.query(qry3));
//Create new Opportunities
for(y=0;y<2;y++){
Opportunity TestOppFilled = new Opportunity(RecordTypeID = rt.id, Name='KBBTestOpp' + y, Account= (Account)testAcct1,
StageName = '3 - On Hold',Id_Surrogate__c = 'KBBTestOpp' + y,
CloseDate = date.parse('3/30/2033'), Pub_Dev_Rep__c = 'Jason');
oppsToInsert.add(TestOppFilled);
Opportunity TestOppEmpty = new Opportunity(RecordTypeID = rt.id, Name= 'KBBTestOpp' + 1000 + y,
Account = (Account)testAcct2, StageName = '3 - On Hold',Id_Surrogate__c = 'KBBTestOpp' + 1000 + y,
CloseDate = date.parse('3/30/2033'), Pub_Dev_Rep__c = 'Jason');
oppsToInsert.add(TestOppEmpty);
}
// Insert Opportunities
try{
system.debug ('trying to insert Opportunities');
database.insert(oppsToInsert);
} catch(DMLException e1) {
system.debug('ERROR: ' + e1);
}
Map<string,Account>oIdToAccountMap = new Map<string, Account>();
for(Sobject z : Trigger.new){
Opportunity Oppty = new Opportunity();
Oppty = (Opportunity)z;
oIdToAccountMap.put(Oppty.Name, AccountMap.get(Oppty.Account.name));
//verify opportunity copy of account data
system.assertEquals(Oppty.Vertical__c,Oppty.Account.Vertical__c );
system.assertEquals(Oppty.Uniques__c,Oppty.Account.Uniques__c );
system.assertEquals(Oppty.Pageviews__c,Oppty.Account.Pageviews__c );
system.assertEquals(Oppty.Display__c,Oppty.Account.Display__c );
system.assertEquals(Oppty.Video_PubDev__c,Oppty.Account.Video__c );
system.assertEquals(Oppty.Mobile__c,Oppty.Account.Mobile__c );
system.assertEquals(Oppty.Monthly_Display_Impressions__c,Oppty.Account.Monthly_Display_Imps__c );
system.assertEquals(Oppty.Monthly_Video_Impressions__c,Oppty.Account.Monthly_Video_Imps__c);
system.assertEquals(Oppty.Monthly_Mobile_Impressions__c,Oppty.Account.Monthly_Mobile_Imps__c );
}
}
}
Thanks in advance. This is my first non-trivial trigger, so I'm a newbie and will appreciate any insight you can offer.
kbb
Thanks to everyone who helped me with this problem -- I understand triggers much better now. The actual problem occurred because I was using a before insert trigger when I needed to be using an after insert trigger.
When the documentation says (paraphrasing here) that system generated fields are not available in Trigger.new in before insert triggers, they are talking about fields that create a relationship as well as formula fields, fields that get changed by workflow rules, etc. Because of the order of execution, at the time I was trying to use information in a relationship field, the relational Account fields were not available. Attempting to use them resulted in a return of NULL, which I could see in the debug log.
An after insert trigger works the way I needed it to in this case. It didn't matter if I used AccountID or the Account.id, Account.name nomenclature -- Trigger.new just didn't have the information needed at the before insert point in time. This was counter-intuative for me because I had filled in the Account field on the opportunity record that fired the trigger but it turns out that this is the way before insert triggers work. The relationships are generated after a record is inserted.
Thank you to everyone who answered my question -- your input helped me with using maps and generally structuring my trigger so it did what I needed done. The discussion board has helped me a lot, and is the first place I look when I encounter a new problem. You all are awesome!
All Answers
Hi KBB,
Rather than building a set of accounts to copy from, you could build a <id,Account> map to pull the values from. I think doing this could greatly reduce the complexity of your code and make testing easier too.
You'll need to add your record type criteria and the Account fields to the map query:
I think this is your 11th line of your trigger
This is not possible in the trigger as the trigger.new won't provide the reference values
You can access only Opprtunity fieds in this case
And if youo need any referenced fields you need to externally query and use them
If this makes your case solved, mark it as a solution
Thanks for your help, Kirk.
I tried the code you sent me, and have pasted what I have in the code window. The problem now is that the tmpSet remains empty because Trigger.new doesn't give me access to the Account field. This problem isdriving me crazy. Do you have any other suggestions for keeping the account mapped to the relevant opportunity? Thank you for your help.
Hi, Hasha_trekbin,
I'm sure you are correct as to the reason for my problems. Can you please explain what you mean by "externally query"? That is, I know how to include a SOQL query in the body of my trigger, but that doesn't get me any Account IDs to map (naturally, since Trigger.New doesn't see this as an Opportunity field. Is there some way to gather the Accounts for mapping outside my code? Here is what I have so far:
Thank you again for your help.
Kathybb
Hi kbb,
On your Opportunity object, are you both using and populating the standard Account field?
I am also learning apex code and I got that working...
please check that you are not assigning ur field values of accountid and accountname.
Can you provide your latest code?
Sure. Here's the trigger:
And here's the test:
I really appreciate your help with this. The weird thing is that the trigger works properly with only one record at a time, but comes up with NULL when I try to query for the related accounts with multiple inserts in the test. I really want to understand what is going on with this because I can a see several places where before insert triggers would be good to use if only I can get this working.
Thank you again,
Kathy
After inserting your opportunities, you need to pull them back into a new list, being sure to include the Account fields in the query.
Using this line as an example, we have no idea what Oppty.Account.Vertical__c is as we've never asked SF for it, resulting in the NULL.
You'll want to do something like this:
Thank you so much for your help.
I changed my test code to to query back the opportunities before the assertions.. My basic problem still remains. The documentation says that on the trigger side of a before insert query, I don't have access to relationship fields, in this case Account.id and Account.name. This trigger is supposed to copy the custom field information from the related account custom fields. However, when I build my set of Account IDs in the trigger itself, it is inserting NULL for all of them and the trigger has no information as to which Account it should be copying from.
Here's the trigger code:
Can you suggest any way to solve this problem? I don't want to use an update trigger because I only want this to happen on insert. I'm probably doing something wrong in all of this, I know, but can you help me figure out the way to make this work?
for completeness, here's my Test code as it stands:
I really appreciate all the help you've given me so far.
Kathy
On the line you pointed out, you have the following instead of what was in my block of code:
Remove the "."
This will keep you on the Opportunity level, rather than trying to traverse the Opportunity-->Account relationship.
Thanks to everyone who helped me with this problem -- I understand triggers much better now. The actual problem occurred because I was using a before insert trigger when I needed to be using an after insert trigger.
When the documentation says (paraphrasing here) that system generated fields are not available in Trigger.new in before insert triggers, they are talking about fields that create a relationship as well as formula fields, fields that get changed by workflow rules, etc. Because of the order of execution, at the time I was trying to use information in a relationship field, the relational Account fields were not available. Attempting to use them resulted in a return of NULL, which I could see in the debug log.
An after insert trigger works the way I needed it to in this case. It didn't matter if I used AccountID or the Account.id, Account.name nomenclature -- Trigger.new just didn't have the information needed at the before insert point in time. This was counter-intuative for me because I had filled in the Account field on the opportunity record that fired the trigger but it turns out that this is the way before insert triggers work. The relationships are generated after a record is inserted.
Thank you to everyone who answered my question -- your input helped me with using maps and generally structuring my trigger so it did what I needed done. The discussion board has helped me a lot, and is the first place I look when I encounter a new problem. You all are awesome!