1

I am having some trouble with writing a trigger to get a related list of Opportunity Line Items IF the stage on Opportunity = Closed Won. Once I get this related list, I need to create a new opportunity with the account field on opportunity being equal to the dealership__c field on opportunity line item. So for example, for dealership 'Test Dealership 1' (Which is an account type lookup) will have some opportunity line items associated with it in the related list on Opportunity like:

Order Item: Wifi Quantity: 2 Ratecard: 30 Sales Price: 360 Total Price: 720 Dealership: Test Dealership 1

Order Item: Mobile Quantity: 4 Ratecard: 69 Sales Price: 828 Total Price: 3312 Dealership: Test Dealership 1

And there can also be another Dealership 'Test Dealership 2'

Order Item: Laptop Quantity: 8 Ratecard: 630 Sales Price: 630 Total Price: 5040 Dealership: Test Dealership 2

What needs to happen: One new opportunity created with account name = Dealership 1, this opportunity should have a related list of opportunity line items with products Wifi and Mobile.

Then another new opportunity should be created with account name = Dealership 2 with a related list of products containing Laptop opportunity line item.

This is my trigger code at the moment:

trigger ParentOppToChild on Opportunity (before insert)
{

    public void OnAfterUpdate(Opportunity[] updatedOpportunities)
    {
        Map<ID,OpportunityLineItem> lineItemDealership = [SELECT id, (SELECT ID, Dealership__c From OpportunityLineItems) FROM Opportunity];
        List<Opportunity> opptys = [SELECT Id, Name, (SELECT Id, PricebookEntry.Product2.Name FROM OpportunityLineItems) FROM Opportunity WHERE Id =: updatedOpportunities];
        Map<String, List<OpportunityLineItem>> opportunityLineItemsMap = new Map<String, List<OpportunityLineItem>>();

        for (Opportunity oppty : [SELECT Id, Name, (SELECT Id, Name, Dealership__c FROM OpportunityLineItems) FROM Opportunity WHERE Id =: updatedOpportunities])
        {
            if(oppty.StageName == 'Closed Won')
            {
                opportunityLineItemsMap.put(oppty.Id, oppty.OpportunityLineItems);
                system.debug(opportunityLineItemsMap);
            }
        }
    }      
}

Is this close to what I need to do?

2

Let me begin by saying that what you're trying to do is not a trivial task.

Next, this would seem to me to need to be an AfterInsert, AfterUpdate trigger. Even if your org creates Opportunities that are already Closed-Won, then you'd still want to create this as an After Insert so you'd have the necessary Id's needed to relate the child Opps to their parents. You just couldn't do that with a Before trigger.

I also see that you've declared a Method within a trigger. You can call a class method from within a trigger to run the logic for you, but you can't declare one. You need to write that code separately. You'd use something like the Tidy Bulkified Trigger Pattern where you'd pass Trigger.new and Trigger.Old and possibly other things to the Class.

Below, I've rewritten your trigger code to show you the gist of the logic you'll want to have for the basic trigger. You can always move that logic to an external class using the above reference as a guide.

trigger ParentOppToChild on Opportunity (AfterInsert, AfterUpdate)
{
   set<Id>oids = new set<Id>();

   For(Opportunity o : trigger.new)
   }
      if(o.StageName = 'Closed-Won' && trigger.oldmap.get(o.Id).StageName != 'Closed-Won`)
      {
         oids.add(o.Id);  
         //get opp.ids where stageName has just changed to Closed Won
       }
   }


   map<Id,Opportunity>OppOLI = new map<Id,Opportunity>([SELECT Id, Name, Account.Id, Account.Name, StageName, Amount,(SELECT Id,Quantity, PricebookEntry.Product2.Name, TotalPrice, Dealership__c FROM OpportunityLineItems FROM Opportunity WHERE Id IN : oids];
   //Have added more fields from OLI since you'll be duplicating them later. You'll probably want more.

   //Extract Line Items
   list<Account>Dealerships = new list<Account>();

   map<Id,Dealership__c>Opp2Deal = new map<Id, Dealership__c>();

   map<Id,Dealership__c>OLI2Deal = new map<Id, Dealership__c>();

   map<Id,OpportunityLineItem>OliMap = new map<Id,OpportunityLineItem>();

   For(Id OppId : OppOLI.keyset())
   {      
       Opportunity opp = OppOLI.get(OppId);

       List<OpportunityLineItem>OLI = opp.OpportunityLineItems;


      for (OpportunityLineItem OppLI : OLI) 
      {

         Account acc = new Account(Parent.Account = Opp.Account.Id, Id = OLI.Dealership__c);
         // I've assumed the Dealers are child Accounts & Dealership__c is a look-up to Acct ID.

         Dealerships.add(acc);

         Opp2Deal.put(opp.Id, Dealership__c);

         OLI2Deal.put(OppLI.Id, Dealership__c,);

         OliMap.put(OppLI.Id, OppLI);

      } 

   }


   //Create new Opps for each Child Opp/Dealer

   list<opportunity>toinsert = new list<opportunity>();
   list<opportunityLineItem>oliInsert = new list<OpportunityLineItem>();    
   Decimal Amt = 0.00 

   For(opportunity o : oids)
   {

      Opportunity op = new Opportunity(Account = Opp2Deal.get(o.Id), Name = o.Name + ' Child', StageName = 'Pending' );
      //Will leave details of Names and Stages to you

      for(OpportunityLineItem oLnI : OLI2Deal.keyset())
      {                     
          if(OLI2Deal.contains(op.Opp2Deal.get(o.Id)){

             for(Id olid : OLI2Deal.keyset())
             {
                if(OLI2Deal.get(olid).Dealership__c = Opp2Deal.get(o.Id)
                {
                   OpportunityLineItem oppLnItm = new OpportunityLineItem(Quantity = OliMap.get(olid).Quantity, PricebookEntry.Product2.Name = OliMap.get(olid).PricebookEntry.Product2.Name, TotalPrice = OliMap.get(olid).TotalPrice);
                   // creates new line item for each matching Dealership__c OLI record
                }
             }

              Amt += oppLnItm.TotalPrice;
              //Calculates total of line items to get Opp Amount

              oliInsrt.add(oppLnItm);
          }  
      }     

      op.Amount = Amt;

      Opp2OLI.put(op,oliInsrt);
      // add each OLI list to map separately to retrieve later to add Opp.ID

      toinsert.add(op);

      Amt = 0.00;
      //reset Amt to 0

    }

    //insert the new Opps
    Insert toinsert;

    integer i = 0;
    list<OpportunityLineItem>oliInlst = new list<OpportunityLineItem>();


    //Add Opp Ids to OLI's before insertion
    for(opportunity opty : toinsert)
    {

        list<OpportunityLineItem>oliInlst = Opp2OLI.get(toinsert[i]);
        //To be safe, using counter since lists in orig map didn't have Ids before insertion

        for(OpportunityLineItem opli : oliInlst)
        {

            opli.OpportunityId = opty.Id;

            OLIinsert.add(opli);
         }

        i++;
     }        

     //Insert related OLI records
     insert OLIinsert;

}// end of trigger

Note: The above code has not been tested or debugged. It is intended as a guide. Check your org for required fields to create these objects. Also be sure to add plenty of debug statements as you proceed.

Edited to add One limitation of this code is that multiple Opps containing the same Dealership__c could be problematic. They might wind up being combined into a single Opportunity even though they may have come from different parents.

This code doesn't create a Parent-Child relationship of the Opps as there isn't a field I know of that does that (didn't see one in the Object Reference). You could choose to create a Parent Opp field to relate them or use the Name of the parent Opp as I did above as the first part of the child Opp's Name to help create the reference. Having a clearly defined Parent-child relationship could be used to solve any issues related to mulitple Opps hitting the trigger containing the same Dealership__c in it's record not being combined in the same new Opportunity.

The only other way I know of around the issue be to use hash code which is an advanced coding method. I strongly suspect introducing hash methods would make it very difficult for you to debug or modify your code in the future.

  • Thanks a lot for your help, I am working through this code to understand and apply it. – Nik Aug 25 '15 at 10:36
  • I had been working on this (spent a LOT more time than intended) and didn't see your other posts until after I finally posted. In the future, please don't create multiple posts on the same question. Instead, edit existing posts to rephrase them, add clarity, code, etc, or put a bounty on it to draw more attention to it. I realize you don't have the needed rep yet to put a bounty on your posts, but you also can't always expect immediate answers to questions like this one. – crmprogdev Aug 25 '15 at 14:51

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.