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
gregusagregusa 

How to insert a new Opportunity with Products in a trigger?

Working on a trigger that creates a new opportunity when a particular type of opportunity is Closed Won.  I can't clone it because a number of things change, but for creating the new opportunity I need to be able to get at the values of not only the opportunity but the product line items.

 

There are two things I'm not sure of:

 

1) Are the product line items included via the Opportunity when I create a map of Opportunities?  If so, how do I reference them later when extracting certain values to apply to the new Opportunity?

 

2) How do I create the line items for the new opportunity?

 

Here's the code I have so far excluding the items referenced above:

 

 

trigger createRepSitesRecurringOpportunity on Opportunity (after insert, after update) { RecordType rt = [SELECT Id FROM RecordType WHERE Name = 'Rep Sites' AND SobjectType = 'Opportunity']; // Create map for Contact ID's & Opportunities, that match our RecordType criteria Map<Id,Opportunity> mapRepSitesOpportunities = new Map<Id, Opportunity>(); // Loop through the Opportunities being inserted and see if there are any for the Rep Sites record type and "Apply Recurring Billing = true". for (Opportunity o : trigger.new) { If ((o.RecordTypeId == rt.Id) && (o.Apply_Recurring_Billing__c == true)){ mapRepSitesOpportunities.put(o.Contact__c, o); } } system.debug('mapRepSitesOpportunities.size at Line 15 = ' + mapRepSitesOpportunities.size()); If (mapRepSitesOpportunities.size() > 0) { //Check for Rep Sites = Active and remove Opportunities from the list where Rep is no longer active. List<Id> contactidsForOpptToNotCreate = new List<Id>(); for (Contact c : [Select Id, Rep_Sites__c from Contact where Id in :mapRepSitesOpportunities.keySet()]) { If (c.Rep_Sites__c <> 'Active') { mapRepSitesOpportunities.remove(c.Id); } } } system.debug('mapRepSitesOpportunities.size at Line 29 = ' + mapRepSitesOpportunities.size()); //Create Oppt with the correct Recurring product/amount and set the Close Date to First Day of the next month If (mapRepSitesOpportunities.size() > 0) { List<Opportunity> opptsToInsert = new List<Opportunity>(); Opportunity newOppt = new Opportunity(); for(Opportunity oppt : [Select RecordTypeId, Name, Contact__c, AccountId, Amount From Opportunity where Id in :mapRepSitesOpportunities.values()]) { newOppt.Name = oppt.Name + ' Recurring Auto Generated'; newOppt.AccountId = oppt.AccountId; newOppt.Contact__c = oppt.Contact__c; newOppt.RecordTypeId = oppt.RecordTypeId; newOppt.CloseDate = Date.today(); //change this to first of next month newOppt.Amount = oppt.Amount; //stop mapping this once the product is created and amount set there. newOppt.StageName = 'Recurring Billing'; opptsToInsert.add(newOppt); } insert(opptsToInsert); } }

 

 Where do I go next?

 

Best Answer chosen by Admin (Salesforce Developers) 
gregusagregusa

Might as well answer my own question...

 

1) Yes the product line items are included in that initial map I made of Opportunities.  To reference them, include the fields you need in your query, loop through each line item, and then reference them like this:

 

 

for(Opportunity oppt : [Select RecordTypeId, Name, Contact__c, AccountId, (Select PricebookEntry.Name From OpportunityLineItems) From Opportunity where Id in :mapRepSitesOpportunities.values()]) { newOppt.Name = oppt.Name + ' Recurring Auto Generated'; newOppt.AccountId = oppt.AccountId; newOppt.Contact__c = oppt.Contact__c; newOppt.RecordTypeId = oppt.RecordTypeId; newOppt.CloseDate = newOpptCloseDate; newOppt.StageName = 'Recurring Billing'; for (OpportunityLineItem lineItem :oppt.OpportunityLineItems) { newOppt.Description = lineItem.PricebookEntry.Name; } if(newOppt.Description <> Null) { opptsToInsert.add(newOppt); } }

 

The only guidance I could find on this was to insert the opportunities then create the line items from the collection of inserted Opportunities.  So that's what I did.

 

 

//only insert the opportunities once they've got products attached to them. if(opptsToInsert.size() > 0) { insert(opptsToInsert); //insert an opportunity line item for each of the new opportunities with the correct product List<OpportunityLineItem> opptLineItemsToInsert = new List<OpportunityLineItem>(); OpportunityLineItem newOpptLineItem = new OpportunityLineItem(); PricebookEntry peWEBinteractive = [SELECT Id, UnitPrice From PricebookEntry WHERE Name = 'WEBinteractive Recurring']; PricebookEntry peWEBbranding = [SELECT Id, UnitPrice From PricebookEntry WHERE Name = 'WEBbranding Recurring']; for(Opportunity opptInserted : [Select Id, Description from Opportunity where Id in :opptsToInsert]) { newOpptLineItem.OpportunityId = opptInserted.Id; newOpptLineItem.Quantity = 1; if(opptInserted.Description.contains('WEBinteractive')) { newOpptLineItem.PricebookEntryId = peWEBinteractive.Id; newOpptLineItem.TotalPrice = peWEBinteractive.UnitPrice; } else { newOpptLineItem.PricebookEntryId = peWEBbranding.Id; newOpptLineItem.TotalPrice = peWEBbranding.UnitPrice; } opptLineItemsToInsert.add(newOpptLineItem); } insert(opptLineItemsToInsert); }

 

 As you can see, I also realized I didn't want to create a new opportunity until it had line items so I removed "after insert" since a newly inserted opportunity would never have line items, then made sure in the update that there were line items before creating any new opportunities.

 

Hope this helps someone!  And if there are any better ways to do this please chime in.

 

All Answers

gregusagregusa

Might as well answer my own question...

 

1) Yes the product line items are included in that initial map I made of Opportunities.  To reference them, include the fields you need in your query, loop through each line item, and then reference them like this:

 

 

for(Opportunity oppt : [Select RecordTypeId, Name, Contact__c, AccountId, (Select PricebookEntry.Name From OpportunityLineItems) From Opportunity where Id in :mapRepSitesOpportunities.values()]) { newOppt.Name = oppt.Name + ' Recurring Auto Generated'; newOppt.AccountId = oppt.AccountId; newOppt.Contact__c = oppt.Contact__c; newOppt.RecordTypeId = oppt.RecordTypeId; newOppt.CloseDate = newOpptCloseDate; newOppt.StageName = 'Recurring Billing'; for (OpportunityLineItem lineItem :oppt.OpportunityLineItems) { newOppt.Description = lineItem.PricebookEntry.Name; } if(newOppt.Description <> Null) { opptsToInsert.add(newOppt); } }

 

The only guidance I could find on this was to insert the opportunities then create the line items from the collection of inserted Opportunities.  So that's what I did.

 

 

//only insert the opportunities once they've got products attached to them. if(opptsToInsert.size() > 0) { insert(opptsToInsert); //insert an opportunity line item for each of the new opportunities with the correct product List<OpportunityLineItem> opptLineItemsToInsert = new List<OpportunityLineItem>(); OpportunityLineItem newOpptLineItem = new OpportunityLineItem(); PricebookEntry peWEBinteractive = [SELECT Id, UnitPrice From PricebookEntry WHERE Name = 'WEBinteractive Recurring']; PricebookEntry peWEBbranding = [SELECT Id, UnitPrice From PricebookEntry WHERE Name = 'WEBbranding Recurring']; for(Opportunity opptInserted : [Select Id, Description from Opportunity where Id in :opptsToInsert]) { newOpptLineItem.OpportunityId = opptInserted.Id; newOpptLineItem.Quantity = 1; if(opptInserted.Description.contains('WEBinteractive')) { newOpptLineItem.PricebookEntryId = peWEBinteractive.Id; newOpptLineItem.TotalPrice = peWEBinteractive.UnitPrice; } else { newOpptLineItem.PricebookEntryId = peWEBbranding.Id; newOpptLineItem.TotalPrice = peWEBbranding.UnitPrice; } opptLineItemsToInsert.add(newOpptLineItem); } insert(opptLineItemsToInsert); }

 

 As you can see, I also realized I didn't want to create a new opportunity until it had line items so I removed "after insert" since a newly inserted opportunity would never have line items, then made sure in the update that there were line items before creating any new opportunities.

 

Hope this helps someone!  And if there are any better ways to do this please chime in.

 

This was selected as the best answer
mechanicalRHINOmechanicalRHINO

I'm looking to create a trigger that does something similar to the process described in this thread. I'm having trouble piecing together the code from your original post and the code in the solution. Can you post the completed final trigger code?

 

Thanks!

iSqFt_ADiSqFt_AD

I know this is an old post but I am looking to do the exact same but am not skilled enough to piece this code together in a working trigger to clone opportunities with their products. Anyone?


Thanks!