+ Start a Discussion
foodrunnerfoodrunner 

Trigger to Pull Product Name to Custom Object upon creation

I am working on a trigger that will create a custom object record when a product is add to the Opportunity, where the product name will be copied to the custom record. I qwould like the product name be the name of the custom record, but the system is returning the record id in the name field. Here is my code:

 

trigger newSpecSheet on OpportunityLineItem (after Insert) {
    List<Manufacturing_Design_Sheet__c> mds = new List<Manufacturing_Design_Sheet__c>();
    for (OpportunityLineItem oli: Trigger.new) {
        if(Pricebookentry.id != null) {
        mds.add(new Manufacturing_Design_Sheet__c (
            Name = oli.Pricebookentry.Product2.name,
            Opportunity_Name__c = oli.Opportunityid,
            RecordTypeId = '012500000001COp',
            Quotation_Item_Number__c = 1));
        
        }
    }
    insert mds;
} 

How do I put the name of the product in the name field on the custom record?

Thank you

Best Answer chosen by Admin (Salesforce Developers) 
sfdcfoxsfdcfox

Your code, while having the best of intentions, makes one minor (and non-obvious) assumption: You assume that PricebookEntry.Product2.Name is automatically available. In reality, this is not true; anything beyond the first member of an object is not automatically passed into the trigger. This is because, like most programming languages, Apex Code is a "lazy" language-- if you do not tell it to query something, it probably will not do so "for free."

 

Consider the following:

 

 

trigger someCustomLogic on Contact (before insert) {
  for(Contact c:Trigger.new) {
    // This is okay.
    c.Account__c = c.AccountId;
    // The two-dot notation means we will not get useful info.
    c.AccountName__c = c.Account.Name;
    // Children are also not available.
    c.RoleCount__c = c.OpportunityContactRoles.size();
  }
}

 

 

If you wanted to make the previous code work, you might do something like the following:

 

 

trigger updateStuff on Contact (before insert) {
  Map<Id,Contact> contactMap =
    new Map<Id,Contact>([select id,accountid,account.name,(select id from opportunitycontactroles) from contact where id in :trigger.new);
  for(Contact c:Trigger.new) {
    // In our example, we could use c.AccountId directly, but this is for illustrative purposes.
    c.Account__c = contactMap.get(c.Id).AccountId;
    // Here, we will actually get the account's name. I test for "null" first, though.
    c.AccountName__c = (contactMap.get(c.Id).Account<>null) ? contactMap.get(c.Id).Account.Name : null;
    // Amazingly, this code will also now work. Same concept as before...
    c.RoleCount__c = contactMap.get(c.Id).OpportuityContactRoles.size();
  }
}

 

Here's some guidelines for data that is (and is not) available in triggers "for free" (does not need to be queried to access):

 

 

 

  • Any SObject type variable that evaluates Trigger.newMap.containsKey(record.Id) or Trigger.oldMap.containsKey(record.Id) to true has all of its non-relationship, non-calculated fields available to it. In other words, if you can call Trigger.anyMap.get(record.Id).SomeField successfully, you will get a real value as long as it was user-supplied and not calculated by the system as a real-time calculation or standard delayed calculation (rollup-summaries). You should not depend on those values because they may not be in sync with the current system (unless you do a query to verify). Note that the SomeField above must be directly accessible from Trigger.anyMap.get(record.Id), if you can not do so, it will be unavailable.
  • Any parent relationship field on the SObject (a standard example is Contact.Account) will have only the ID of the associated record available. In this sense, it is treated as a normal text field with an additional validation to ensure that the entered value matches a possible option. For example, no user may attach an opportunity into Contact.Account, nor could you enter a value such as '12345.' If this field contains a value, it is assumed that the related record exists and, if you use with sharing, the user has sufficient access to. Note that the Id field is an alias for the relationship, so ID is the exception to the first rule above. Contact.AccountId is the same as Contact.Account.Id, and Account.License__c is identical to Account.License__r.Id.
  • Any child relationship field on the SObject (a standard example is Contact.OpportunityContactRoles) is never available, even if the children caused the update (i.e. a recursive trigger, or some business logic recursive triggers such as the one for OpportunityLineItem entries). If you want the values, you must do a query.
  • Any other value that I have not explicitly mentioned here is likely not available without a query. However, do see System, UserInfo, and other help topics in the documentation to validate if it really is not possible to do what you are trying to do without wasting queries. For example, some user information is available "without cost", saving you one of your precious DB queries.
And if you give up, here's the answer to your question:
trigger.newSpecSheet on OpportunityLineItem (after insert) {
  List<Manufacturing_Design_Sheet__c> mds = new List<Manufacturing_Design_Sheet__c>();
  Id recordType = null;
  for(RecordType rt:[select id,name from RecordType where SObject='Manufacturing_Design_Sheet__c' and DeveloperName='MDS_SIMPLE']) {
    recordType = rt.Id;
  for(OpportunityLineItem oli:[Select Id,PricebookEntry.Product2.Name,Opportunity.Id From OpportunityLineItem where Id In :Trigger.new]) {
    if(oli.PricebookEntry<>null) {
      mds.ass(new Manufacturing_Design_Sheet__c (
        Name = oli.PricebookEntry.Product2.Name, Opportunity_Name__c = oli.Opportunity.Id, recordTypeId = rt.Id,, Qutation_Item_Number__c = 1));
    }
  }
  insert mds;
}

Note that you should never embed an ID. You're asking for headaches later. Take the one-query hit and optimize elsewhere to make up for it.

All Answers

sfdcfoxsfdcfox

Your code, while having the best of intentions, makes one minor (and non-obvious) assumption: You assume that PricebookEntry.Product2.Name is automatically available. In reality, this is not true; anything beyond the first member of an object is not automatically passed into the trigger. This is because, like most programming languages, Apex Code is a "lazy" language-- if you do not tell it to query something, it probably will not do so "for free."

 

Consider the following:

 

 

trigger someCustomLogic on Contact (before insert) {
  for(Contact c:Trigger.new) {
    // This is okay.
    c.Account__c = c.AccountId;
    // The two-dot notation means we will not get useful info.
    c.AccountName__c = c.Account.Name;
    // Children are also not available.
    c.RoleCount__c = c.OpportunityContactRoles.size();
  }
}

 

 

If you wanted to make the previous code work, you might do something like the following:

 

 

trigger updateStuff on Contact (before insert) {
  Map<Id,Contact> contactMap =
    new Map<Id,Contact>([select id,accountid,account.name,(select id from opportunitycontactroles) from contact where id in :trigger.new);
  for(Contact c:Trigger.new) {
    // In our example, we could use c.AccountId directly, but this is for illustrative purposes.
    c.Account__c = contactMap.get(c.Id).AccountId;
    // Here, we will actually get the account's name. I test for "null" first, though.
    c.AccountName__c = (contactMap.get(c.Id).Account<>null) ? contactMap.get(c.Id).Account.Name : null;
    // Amazingly, this code will also now work. Same concept as before...
    c.RoleCount__c = contactMap.get(c.Id).OpportuityContactRoles.size();
  }
}

 

Here's some guidelines for data that is (and is not) available in triggers "for free" (does not need to be queried to access):

 

 

 

  • Any SObject type variable that evaluates Trigger.newMap.containsKey(record.Id) or Trigger.oldMap.containsKey(record.Id) to true has all of its non-relationship, non-calculated fields available to it. In other words, if you can call Trigger.anyMap.get(record.Id).SomeField successfully, you will get a real value as long as it was user-supplied and not calculated by the system as a real-time calculation or standard delayed calculation (rollup-summaries). You should not depend on those values because they may not be in sync with the current system (unless you do a query to verify). Note that the SomeField above must be directly accessible from Trigger.anyMap.get(record.Id), if you can not do so, it will be unavailable.
  • Any parent relationship field on the SObject (a standard example is Contact.Account) will have only the ID of the associated record available. In this sense, it is treated as a normal text field with an additional validation to ensure that the entered value matches a possible option. For example, no user may attach an opportunity into Contact.Account, nor could you enter a value such as '12345.' If this field contains a value, it is assumed that the related record exists and, if you use with sharing, the user has sufficient access to. Note that the Id field is an alias for the relationship, so ID is the exception to the first rule above. Contact.AccountId is the same as Contact.Account.Id, and Account.License__c is identical to Account.License__r.Id.
  • Any child relationship field on the SObject (a standard example is Contact.OpportunityContactRoles) is never available, even if the children caused the update (i.e. a recursive trigger, or some business logic recursive triggers such as the one for OpportunityLineItem entries). If you want the values, you must do a query.
  • Any other value that I have not explicitly mentioned here is likely not available without a query. However, do see System, UserInfo, and other help topics in the documentation to validate if it really is not possible to do what you are trying to do without wasting queries. For example, some user information is available "without cost", saving you one of your precious DB queries.
And if you give up, here's the answer to your question:
trigger.newSpecSheet on OpportunityLineItem (after insert) {
  List<Manufacturing_Design_Sheet__c> mds = new List<Manufacturing_Design_Sheet__c>();
  Id recordType = null;
  for(RecordType rt:[select id,name from RecordType where SObject='Manufacturing_Design_Sheet__c' and DeveloperName='MDS_SIMPLE']) {
    recordType = rt.Id;
  for(OpportunityLineItem oli:[Select Id,PricebookEntry.Product2.Name,Opportunity.Id From OpportunityLineItem where Id In :Trigger.new]) {
    if(oli.PricebookEntry<>null) {
      mds.ass(new Manufacturing_Design_Sheet__c (
        Name = oli.PricebookEntry.Product2.Name, Opportunity_Name__c = oli.Opportunity.Id, recordTypeId = rt.Id,, Qutation_Item_Number__c = 1));
    }
  }
  insert mds;
}

Note that you should never embed an ID. You're asking for headaches later. Take the one-query hit and optimize elsewhere to make up for it.

This was selected as the best answer
giri rockzzzzgiri rockzzzz

This might help you....

 

trigger newSpecSheet on Opportunity (after Insert) {


    List<Manufacture__c> mds = new List<Manufacture__c>();


    Product__c nn;
    
    for (Opportunity oli: Trigger.new) {

        if(oli.Product__c != null) {


        nn=[select Name from Product__c where id=:oli.product__c];


        mds.add(new Manufacture__c (Name =nn.name));
        }
    }
    insert mds;
}

sfdcfoxsfdcfox

giri,

 

You should always avoid placing queries inside of a "for" loop, because you will run into governor limits when using the data loader or any mass-update integration.

 

It is a best practice to use the following pseudo-code:

 

1) Gather all record ID values you will use.

2) Query all records, placing them into a map.

3) Use the map to look up values that you queried.

 

This process is sometimes referred to as "bulkifying" code (writing code that supports multiple record updates).

giri rockzzzzgiri rockzzzz

Hi sfdcfox, 

 

I am new to apex coding .

Can you tell me what is governers limit.???

Imran MohammedImran Mohammed

You can find detailed info on Governor Limits here.

foodrunnerfoodrunner

Wonderful. Thank you very much for your help.