[SalesForce] Updating OpportunityLineItem Quantity with a fixed TotalPrice

I'm seeing a weird change in an OpportunityLineItem's TotalPrice when changes are made to the Quantity field.

These records have an additional custom field that holds a fixed rate price that is entered by the user. When the OpportunityLineItem is edited via a Visualforce page the Quantity is updated via an <apex:inputText> and the controller sets the TotalPrice to the fixed rate price that the user entered.

After saving the OLI for an existing record, the TotalPrice is changing to be the Quantity * UnitPrice, even though the code that just did the update explicitly set the Quantity and TotalPrice fields. I'd expect the UnitPrice to change instead as it wasn't explicitly set in code.

I can replicate this with anonymous Apex:

integer newQuantity = 17;

OpportunityLineItem oli = [Select Id, UnitPrice, TotalPrice, Quantity from OpportunityLineItem where Id = '00k4000000u8Uvm'];
decimal tp = oli.TotalPrice;
System.assertNotEquals(newQuantity, oli.Quantity, 'Expected to be changing just the Quantity');

System.debug('Before TotalPrice: ' + oli.TotalPrice +
            '\n Quantity: ' + oli.Quantity);

oli.Quantity = newQuantity;
// Keep the TotalPrice unchanged
oli.TotalPrice = tp;

update oli;

OpportunityLineItem oliAfterUpdate = [Select Id, UnitPrice, TotalPrice, Quantity from OpportunityLineItem where Id = '00k4000000u8Uvm'];
System.debug('After TotalPrice: ' + oliAfterUpdate.TotalPrice +
            '\n Quantity: ' + oliAfterUpdate.Quantity +
            '\n UnitPrice:' + oliAfterUpdate.UnitPrice);
System.assertEquals(tp, oliAfterUpdate.TotalPrice);

The assertion is failing above. The TotalPrice is changing rather than the UnitPrice.

There are no revenue schedule record for the OpportunityLineItem.

Note that a subsequent update to the Quantity with the original TotalPrice will update the UnitPrice as expected. It's only if the TotalPrice is the same as the currently stored value that the UnitPrice gets fixed.

From the Docs:

Totalprice
If you do not specify UnitPrice, this field is required. If you specify Discount and Quantity, this field or UnitPrice is required. When updating these records, you can change either this value or the UnitPrice, but not both at the same time.

This field is nillable, but you can’t set both TotalPrice and UnitPrice to null in the same update request.

Best Answer

It appears that including the UnitPrice in the SOQL query the retrieves the OpportunityLineItem is enough to have it override the TotalPrice if the latter is unchanged. It the TotalPrice is changed it will cause the UnitPrice to be recalculated.

So changing:

OpportunityLineItem oli = [Select Id, UnitPrice, TotalPrice, Quantity from OpportunityLineItem where Id = '00k4000000u8Uvm'];

To:

OpportunityLineItem oli = [Select Id, TotalPrice, Quantity from OpportunityLineItem where Id = '00k4000000u8Uvm'];

will allow the anonymous apex test to pass.

I tried using sObject.put to clear out the UnitPrice field, but it didn't work. The other alternative I got to work was to create an entirely new OpportunityLineItem record and only set the Id, Quantity, and TotalPrice fields on it before passing it off to update.

Related Topic