[SalesForce] Creating an OrderItem from a QuoteLineItem

I'm trying to write a trigger that – when a Contract is activated it's accepted quote is converted into an order, under which the QuoteLineItems are now "copied" into OrderItems (order products to the UI) I have a trigger that gets my Contract and Quote ready, and then a method to create the order and orderitems, but I am getting a wildly frustrating error message when I fire this off:

Insert failed. First exception on row 0; first error: INVALID_CROSS_REFERENCE_KEY, invalid cross reference id: []: Class.OrderBuilder.CreateOrderForContract: line 47

Line 47 is my DML call to insert the OrderItems. I'm fairly sure this is something to do with the PriceBookEntryId, but I have been through my code/process and all the Quote/Contract/QuoteLineItems are properly formed, with PriceBook2Ids and correctly cross referenced (as far as I can tell).

Here is the code that does the order/line creation:

Order order = new Order(Name = 'test', Status = 'Draft',
                        AccountId = accountId,
                        EffectiveDate = Date.TODAY(),
                        ContractId = contract.Id,
                        QuoteId = quoteId,
                        PriceBook2Id = PriceBookEntries.stdPricebookId,
                        OpportunityId = [SELECT OpportunityId FROM Quote WHERE Id = :quoteId].OpportunityId
);

insert order;

List<QuoteLineItem> qlis = QuoteLineItems.selectByQuoteId(quoteId);

List<OrderItem> orderProds = new List<OrderItem>();
for(QuoteLineItem thisItem : qlis)
{
    OrderItem newOi = new OrderItem(OrderId = order.Id,
                                    UnitPrice = thisItem.Configured_Sales_Price__c,
                                    Recurring_Price__c = thisItem.Recurring_Sales_Price__c,
                                    QuoteLineItemId = thisItem.Id,
                                    Quantity = thisItem.Quantity,
                                    PricebookEntryId = thisItem.PricebookEntryId
                                   );

    orderProds.add(newOi);
}

insert orderProds;

Now – first, this is just demo-ware, so excuse the inline SOQL under a trigger call. Also, PriceBookEntries.stdPricebookId and QuoteLineItems.selectByQuoteId(quoteId) are utility methods, and have been tested/logged out and are correct. Everything IS correct, but I just get this bloomin' error message. Any hints, tips, advise on what I've missed?

Best Answer

Ok. So it turned out this was a PriceBook2Id problem.

The PriceBookEntryId value being set into the OrderItem references the Pricebook that the QuoteLineItem (and thus the quote) was built on...

As you can kinda see in my code, the PriceBook2Id that I was setting into the Order from my utility method loads the Standard Price book (using the isStandard flag).

This org/demo should only have one pricebook, which is why I thought everything was/would be fine using the Standard PriceBook Id, but here's the magic - this Org had TWO pricebooks (for a completely unknown reason) one called "Standard", and one called "Standard Price Book" - apparently this is a common thing to happen if the actual standard price book is not active when you start creating quotes/products, and is obviously very hard to spot, as they are both called Standard.

pricebook list

So my Quotes were being created with the active Pricebook called "Standard" and then my utility method/SOQL was returning the actual "Standard Price Book" - so the IDs between the Order Pricebook and the QuoteLineItem Pricebooks were different.

To fix it, I simply have to set the Order Pricebook2Id to be that of the quote (which is better going forward anyway) so I (despicably) added more inline SOQL to the Order construction at the top of the code:

Pricebook2Id = [SELECT Pricebook2Id FROM Quote WHERE Id = :quoteId].Pricebook2Id,

So this now ALWAYS uses exactly the same PriceBook as the quote. Problem solved, and it only took me about 5 hours of debugging (and then I found the problem within half an hour of posting on SFSE!)

Related Topic