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.

2 Answers 2


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'];


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.


I did this class to update the total price based on a simple opeation (quantity * days[Customfield] * UnitPrice). To update the total has to be in After Insert or Update Trigger event, but also if you wanna keep the list price as historic you should use the setUnitPrice method on Before insert / update, because for me it was incrementing in every update.

Hope it helps.

public class OpportunityLineItem_Controller {

     * [setUnitPrice description]
     * @param items [description]
    public void setUnitPrice(OpportunityLineItem[] 

items,OpportunityLineItem[] items_old){

        for(OpportunityLineItem item:items){

                item.Precio_de_Venta_custom__c = item.UnitPrice;

     * Update Ammount in OpportunityLineItem based on Days field
     * @param items [description]
    public void updateAmount(OpportunityLineItem[] items,OpportunityLineItem[] items_old){
        System.debug('OLD: '+items_old);
        Set<Id> iditem                     = new Set<Id>();
        Set<Id> idPBE                     = new Set<Id>();
        List<OpportunityLineItem> itemlist = new List<OpportunityLineItem>();
        Map<String,Decimal> mapPBE = new Map<String,Decimal>();
        Map<String,Decimal> mapUnitOriginal = new Map<String,Decimal>();

        Map<String,OpportunityLineItem> map_oldInfo = new Map<String,OpportunityLineItem>();

            for(OpportunityLineItem item:items_old){

        for(OpportunityLineItem item:items){

        for(OpportunityLineItem item:[Select Id,PricebookEntryId,Dias__c,Precio_de_Venta_custom__c,Quantity,UnitPrice,TotalPrice from OpportunityLineItem where Id IN:iditem]){         

                    if(item.Dias__c > 0 && item.Quantity > 0){
                        item.TotalPrice = ((item.Dias__c * item.Quantity) * item.Precio_de_Venta_custom__c);

            else if(Trigger.isUpdate){
                    item.TotalPrice = ((map_oldInfo.get(item.Id).Dias__c * map_oldInfo.get(item.Id).Quantity) * map_oldInfo.get(item.Id).Precio_de_Venta_custom__c);
                    item.Precio_de_Venta_custom__c = map_oldInfo.get(item.Id).Precio_de_Venta_custom__c;
        update itemlist;

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.