Any special reason why you can't have a small trigger?
trigger oppWorkaround on Opportunity(before insert, before update){
for(Opportunity o : trigger.new){
if(o.Pricebook2Id == null){
o.Pricebook2Id = 'hardcoded id here';
}
}
}
Put the id of your standard pricebook and you're done (except developer sandboxes). Bit better way would be to waste 1 query to SELECT Id FROM Pricebook2 LIMIT 1
.
You will still need to specify PricebookEntries for products you want to be selectable (but since you mention standard prices I assume you're aware of that).
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.
Best Answer
Saturn Price Book is a custom Price Book. Standard Price Book is as the name suggests, is the standard Price Book containing all Products and their standard price.
An opportunity can only have one Price Book selected at a time. It is possible to have the Price Book defaulted by some other configuration/customisation in your org so the user does not need to make a conscious decision to select it each time.
You can change Price Books, but doing this will remove all Opportunity Products as you cannot mix Pricebooks on a single Opportunity.
Does this help?