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
iKnowSFDCiKnowSFDC 

Child records not inserting when parent record is created from trigger

Developer Community:  

 

I have a trigger on the Opportunity object that inserts opportunity line items as assets on the account which is working as expected.  A second trigger on the asset object needs to insert a list of child objects called Asset Components to each asset. If I manually insert an asset (without using the opportunity trigger), the asset trigger works as expected.  If I insert the records via the trigger, the child records are not created.  I'm not sure what I'm missing here - any thoughts? 

 

Opportunity trigger works as expected, all assets are inserted: 

trigger createAsset on Opportunity (after update) {
	
	List<Opportunity> opptys = new List<Opportunity>();
	List<OpportunityLineItem> opptyLIS = new List<OpportunityLineItem>();
	List<Asset> assets2Insert = new List<Asset>();
	List<Product_Component__c> pcs = new List<Product_Component__c>();
	List<Asset_Component__c> assetComps2Insert = new List<Asset_Component__c>();
	List<Asset> ats = new List<Asset>();	
	
	for(Opportunity o : trigger.new){
		if((o.IsWon == true)&&(o.Status__c=='Shipped')){
			opptys.add(o);
		}
	}
	
	if(!opptys.isEmpty()){
		for(Opportunity os:opptys){
		opptyLIS = [SELECT id, OpportunityId, Opportunity.Accountid, PriceBookEntryid, Quantity,
					PricebookEntry.Product2id, Opportunity.CloseDate, PricebookEntry.Product2.ProductCode
					FROM OpportunityLineItem 
					WHERE OpportunityId = :os.id];
		}
		
		for(OpportunityLineItem lis : opptyLIS){
			Integer lineQty=integer.valueOf(lis.Quantity);
			
			for(Integer i=0; i<lineQty; i++){
					Asset newAsset = new Asset();
					newAsset.Name = lis.PricebookEntry.Product2.ProductCode;
					newAsset.AccountId = lis.Opportunity.AccountId;
					newAsset.Product2Id = lis.PricebookEntry.Product2Id;
					newAsset.H_W_Exp_Date__c = lis.Opportunity.CloseDate.addyears(1);
					newAsset.S_W_Exp_Date__c = lis.Opportunity.CloseDate.addyears(2);
				assets2Insert.add(newAsset);
			}
		}
	}
	
	insert assets2Insert;
}

 

Asset trigger - works when asset is inserted manually, but not when inserted by above trigger: 

trigger createAssetComponents on Asset (after insert) {
	
	List<Product_Component__c> pcs = new List<Product_Component__c>();
	List<Asset_Component__c> assetComps2Insert = new List<Asset_Component__c>();
	List<Asset> ats = new List<Asset>();

	
	for(Asset a : trigger.new){
		ats.add(a);

	}
	if(!ats.isEmpty()){
		for(Asset asInTrigger : ats){
		pcs=[SELECT Top_Level_Product__c, Quantity__c, Component__c, 
						Component_Name__c, Name
						FROM Product_Component__c 
						WHERE Top_Level_Product__c = :asInTrigger.Product2Id]; 
				
		}
		
		for(Asset ats2 : ats){
			for(Product_Component__c p : pcs){
	
				for(integer c=0; c< p.Quantity__c; c++ ){
					Asset_Component__c assetComps = new Asset_Component__c();
					assetComps.Parent_Asset__c = ats2.id;
					assetComps.Installed__c = date.today();
					assetComps.xAsset_Component__c = p.id;	
					assetComps.Serial_Number__c = 'fill in'+c;
					assetComps2Insert.add(assetComps);	
				}    
			}
		updatedAsset.add(ats2);
	}
	insert assetComps2Insert;
	}	
}

 Thanks for your assistance!

JoAnn

Best Answer chosen by Admin (Salesforce Developers) 
dmchengdmcheng

I think the problem is because your pcs = [select] line is inside a for loop.  If you insert a single asset, then pcs is selected as you expect.  But you have a bunch of assets, then the for loop cycles through all pcs statements and you are left with just the last pcs list, thereby skipping all the previous ones.

All Answers

dmchengdmcheng

I'm having trouble understanding your data structure regarding product components and asset components, but here are a couple areas in the asset trigger that can be improved upon:

 

1.  The pcs = [select ...] line should be outside the for loop, and you should use WHERE Top_Level_Product__c in :SetOfIDs

 

2.  I don't understand your triple set of for loops.  You don't seem to reference any link between asset component and product component.

iKnowSFDCiKnowSFDC

Hi dmcheng, thank you for your feedback.  Reworking with your suggestions and trying to cleanup the triple loop.  

 

Regarding your second question: This org has a custom object called Product Components which is used to build out a bill of materials.  The items on the BOM are serialized and tracked separately and need to be related to the asset.  If a single product2id has 3 product components, there may be 12 of one of the components installed in the top-level unit.  By having the multiple loops, I get the right number of asset components entered for each component type where if I don't have that loop I only get one record for each asset component type.  

 

What I'm completely confused about is that the createAssetComponents trigger works fine if I manually insert an asset.  But if I insert an asset with the createAsset trigger, the second one doesn't fire and no components are created.  

Thanks again for your help and feedback. 

 

JoAnn

dmchengdmcheng

I have not worked with Assets before -- if you insert an asset record, is Salesforce supposed to update the related Opp, and if so I'm curious what field is updated.

iKnowSFDCiKnowSFDC

Not necessarily - but I have the asset creation being trigger by the opportunity being set to closed won and with an build status of shipped.  One org I worked on linked the opportunity and the asset, but this one does not. I just checked the schema builder in this org and Opportunities are definitely not related to Product other than through Opportunity Line Items. 

dmchengdmcheng

I think the problem is because your pcs = [select] line is inside a for loop.  If you insert a single asset, then pcs is selected as you expect.  But you have a bunch of assets, then the for loop cycles through all pcs statements and you are left with just the last pcs list, thereby skipping all the previous ones.

This was selected as the best answer
iKnowSFDCiKnowSFDC

Thank you - that worked exactly like you said. :) I got too hung up on how I would match up the product component to the right product and couldn't see my way past that loop. \

 

JoAnn