function readOnly(count){ }
Starting November 20, the site will be set to read-only. On December 4, 2023,
forum discussions will move to the Trailblazer Community.
+ Start a Discussion
ElkeElke 

Problem with TotalPrice in OpportunityLineItem

Hi everybody,

I have a problem with updating Opportunity Products. User can change the Discount, the Quantity and the Sales Price (in special cases only). The Total Price shall be calculated by the system and displayed at the VF page. In some cases Discount can be set to 100%. If so, I detected the issue that  the Sales Price (OpportunityLineItem.UnitPrice, so I call it UnitPrice in the following description) is set to null by the system during the Update.

In Forum Threads and in the Documentation of the OpportunityLineItem Object I read that UnitPrice and TotalPrice must not be set both during an Update. That’s why I force the TotalPrice to be null before I update a record which is selected from the database before. Indead, this solves the problem. The UnitPrice is stored into the database as expected and TotalPrice is calculated to 0.0 which is correct. Unfortunately the next update in exactly the same manner is failing. I get an exception that the TotalPrice must not be null…

 

I narrowed down the behavior with a simple test class in a developer edition. There are no custom fields, no triggers and no validation rules for the object OpportunityLineItem.

 

Here is the code of my test class mixed with the corresponding Debug information picked out from Apex Test Runner window in the IDE (Eclipse).

 

    static testMethod void myUnitTest() {
        // insert a dummy Product with PricebookEntries then check that they were created
        Product2 pr1 = new Product2(Name='Unit Test Product PR1 Dummy');
        insert pr1;
 
 	//Get the PriceBook
 	PriceBook2 pb=[Select Name, IsStandard, IsActive, Id From Pricebook2 Where IsStandard=true Limit 1];
 	PricebookEntry pe1 = New PriceBookEntry(Product2Id=pr1.Id,PriceBook2Id=pb.Id,unitPrice=444.0,isactive=true);
 	insert pe1; 
        
     
        Account ac1 = new Account(Name='Unit Test Parent');
        insert ac1;
        
        Opportunity op1 = new Opportunity(Name = 'Unit Test Parent', StageName='Prospecting', 
        CloseDate=System.today().addDays(1), AccountId=ac1.Id);
        insert op1;
        Opportunity op1s = [SELECT ID FROM Opportunity WHERE Id=:op1.Id];
 
        //check calculation Prices
        OpportunityLineItem ol1 = new OpportunityLineItem(OpportunityId=op1.Id, 
            UnitPrice=123.00, Quantity=1,Description=null, Discount=100, PricebookEntryId=pe1.Id);

        System.debug(LoggingLevel.INFO, '**********Before Insert Id=null ListPrice= '
        +ol1.ListPrice+' UnitPrice=' + ol1.UnitPrice+' Discount='+ol1.Discount+' TotalPrice='+ol1.TotalPrice);

 

 

14:17:14.598 (1598203000)|USER_DEBUG|[27]|INFO|**********Before Insert Id=null ListPrice= null UnitPrice=123.00 Discount=100 TotalPrice=null

        insert ol1;

       OpportunityLineItem ol2=[Select  o.UnitPrice, o.TotalPrice,
       o.Quantity, o.PricebookEntryId, o.ListPrice,
       o.Id, o.Discount From OpportunityLineItem o where o.id=:ol1.Id];
        
        System.debug(LoggingLevel.INFO, '**********After Insert Id='+ol2.Id+
        ' ListPrice= '+ol2.ListPrice+' UnitPrice=' + ol2.UnitPrice+
        ' Discount='+ol2.Discount+' TotalPrice='+ol2.TotalPrice);

 

14:17:14.688 (1688622000)|USER_DEBUG|[34]|INFO|**********After Insert Id=00kG000000HBCU9IAP ListPrice= 444.00 UnitPrice=123.00 Discount=100.00 TotalPrice=0.00

 

OKAY

 

 		Update ol2;
 		
 
        OpportunityLineItem  ol3=[Select  o.UnitPrice, o.TotalPrice,
        o.Quantity, o.PricebookEntryId, o.ListPrice, o.Id,
        o.Discount From OpportunityLineItem o where o.id=:ol2.Id];
 
        System.debug(LoggingLevel.INFO, '**********After Update '
        +ol3.Id+'ListPrice= '+ol3.ListPrice+ ' UnitPrice=' 
        + ol3.UnitPrice+' Discount='+ol3.Discount+' TotalPrice='
        +ol3.TotalPrice);

 

14:17:14.739 (1739783000)|USER_DEBUG|[42]|INFO|**********After Update 00kG000000HBCU9IAPListPrice= 444.00 UnitPrice=null Discount=100.00 TotalPrice=0.00

 

Problem: UnitPrice is set to null. Trying to hide this by setting TotalPrice to null:

 

	//Try to set TotalPrice to null  because it's 0.0 after update
 	ol2.TotalPrice=null;
  	Update ol2;
 		
 	ol3=[Select  o.UnitPrice, o.TotalPrice, o.Quantity, 
          o.PricebookEntryId, o.ListPrice,o.Id, o.Discount 
          From OpportunityLineItem o where o.id=:ol2.Id];
        
        System.debug(LoggingLevel.INFO,
        '**********After Update with TotalPrice set to null '
         +ol3.Id+'ListPrice= '+ol3.ListPrice+ ' UnitPrice=' 
         + ol3.UnitPrice+' Discount='+ol3.Discount+' TotalPrice='
         +ol3.TotalPrice);

 

14:17:14.790 (1790286000)|USER_DEBUG|[50]|INFO|**********After Update with TotalPrice set to null 00kG000000HBCU9IAPListPrice= 444.00 UnitPrice=123.00 Discount=100.00 TotalPrice=0.00

 

OKAY! But what if user updates a second time:

 

 	//Try to set TotalPrice to null again because it's 0.0 after update
  	ol2.TotalPrice=null;
  	Update ol2;

	ol3=[Select  o.UnitPrice, o.TotalPrice, o.Quantity, 
          o.PricebookEntryId, o.ListPrice,o.Id, o.Discount 
          From OpportunityLineItem o where o.id=:ol2.Id];
 
         System.debug(LoggingLevel.INFO, 
        '**********After 2. Update with TotalPrice set to null '
        +ol3.Id+'ListPrice= '+ol3.ListPrice+ ' UnitPrice=' 
        + ol3.UnitPrice+' Discount='+ol3.Discount+' TotalPrice='
        +ol3.TotalPrice);
 
    }
}

 

14:17:15.099 (2099067000)|FATAL_ERROR|System.DmlException: Update failed. First exception on row 0 with id 00kG000000HBCU9IAP; first error: FIELD_INTEGRITY_EXCEPTION, field integrity exception: TotalPrice (total price must be specified): [TotalPrice]

 

Why can't I update the same record twice with TotalPrice set to null? Maybe I'm wrong with anything...

 

Any explainings for this behavior? I urgently need an idea how to solve this trouble. Seems to be the same problem in Winter’12 and Spring ’12.

 

Thanks in advance,

Elke

craigmhcraigmh

From what I remember when playing around with these fields, updating one also updates the other.

 

So when you're setting TotalPrice to null, it's doing a lot of other work that you're probably not anticipating.

 

In your first call, you're not setting TotalPrice to null, you're just not assigning it. Why don't you do that in your second call? What is the point of setting it to null explicitly?

ElkeElke

My application does following (not exciting things, a very common workflow): Selecting all OpportunityLineItems for an Opportunity from the database, display at a VF Page. If a user is changing something (e.g. Discount or Quantity) this OpportunityLineItem has to be updated. Since this record has been already inserted and selected again, the field TotalPrice is filled with a value (in case of 100% Discount it's filled with 0.0). Updating this record with this TotalPrice, Unitprice and all other fields results in setting UnitPrice to null during the Save. That's why I tried to set TotalPrice to null before updating - at least it shall be calculated once again during the Save based on the actual field values...

 

Tried one thing more today: created a new OpportunityLineItem record, filled all my sected values in it but without assigning a TotalPrice as you suggested. In this case I have the problem again with the UnitPrice is set to null:

 

       //check calculation Prices
        OpportunityLineItem ol1 = new OpportunityLineItem(OpportunityId=op1.Id, UnitPrice=123.00, Quantity=1,
            Description=null, Discount=100, PricebookEntryId=pe1.Id);
        System.debug(LoggingLevel.INFO, '**********Before Insert Id=null ListPrice= '+ol1.ListPrice+' UnitPrice=' + ol1.UnitPrice+' Discount='+ol1.Discount+' TotalPrice='+ol1.TotalPrice);

        insert ol1;

       OpportunityLineItem ol2=[Select  o.UnitPrice, o.TotalPrice, o.OpportunityId, o.Description, o.Quantity, o.PricebookEntryId, o.ListPrice,
        o.Id, o.Discount From OpportunityLineItem o where o.id=:ol1.Id];
        
        System.debug(LoggingLevel.INFO, '**********After Insert Id='+ol2.Id+' ListPrice= '+ol2.ListPrice+' UnitPrice=' + ol2.UnitPrice+' Discount='+ol2.Discount+' TotalPrice='+ol2.TotalPrice);
 		
 		
 		//try to create a new record, do not assign TotalPrice
 		OpportunityLineItem OlNew= new OpportunityLineItem(Id=ol2.Id,  OpportunityId=ol2.OpportunityId, UnitPrice=ol2.UnitPrice, Quantity=ol2.Quantity,
            Description=ol2.Description, Discount=ol2.Discount, PricebookEntryId=ol2.PriceBookEntryId);
 
        System.debug(LoggingLevel.INFO, '**********Before Insert of Record Copy Id='+olNew.Id+' ListPrice= '+olNew.ListPrice+' UnitPrice=' + olNew.UnitPrice+' Discount='+olNew.Discount+' TotalPrice='+olNew.TotalPrice);
 		
 		Upsert olNew;
 		
 
        OpportunityLineItem  ol3=[Select  o.UnitPrice, o.TotalPrice, o.Quantity, o.PricebookEntryId, o.ListPrice,
        o.Id, o.Discount From OpportunityLineItem o where o.id=:olNew.Id];
 
        System.debug(LoggingLevel.INFO, '**********After Upsert Record Copy '+ol3.Id+'ListPrice= '+ol3.ListPrice+ ' UnitPrice=' + ol3.UnitPrice+' Discount='+ol3.Discount+' TotalPrice='+ol3.TotalPrice);

 results in following:

07:46:37.168 (1168081000)|USER_DEBUG|[34]|INFO|**********After Insert Id=00kG000000HBEdYIAX ListPrice= 444.00 UnitPrice=123.00 Discount=100.00 TotalPrice=0.00

 

07:46:37.169 (1169129000)|USER_DEBUG|[40]|INFO|**********Before Insert of Record Copy Id=00kG000000HBEdYIAX ListPrice= null UnitPrice=123.00 Discount=100.00 TotalPrice=null

 

07:46:37.216 (1216018000)|USER_DEBUG|[48]|INFO|**********After Upsert Record Copy 00kG000000HBEdYIAXListPrice= 444.00 UnitPrice=null Discount=100.00 TotalPrice=0.00

 

Any help is highly appreciated, thanks.

Elke

craigmhcraigmh

Hmmm, yeah...that does seem fairly straightforward, but complicated by how Salesforce updates the OpportunityLineItem object automatically. I'd say that your best bet is to look at the actions that Salesforce takes, play around with it a bit, and you should get a handle on what actions produce which results.

 

http://www.salesforce.com/us/developer/docs/api/Content/sforce_api_objects_opportunitylineitem.htm

 

  • If you specify Discount without specifying TotalPrice, the TotalPrice is adjusted to accommodate the new Discount value, and the UnitPrice is held constant.
  • If you specify both Discount and Quantity, you must also specify either TotalPrice or UnitPrice so the system knows which one to automatically adjust.
  • If you specify Quantity without specifying the UnitPrice, the UnitPrice value will be adjusted to accommodate the new Quantity value, and the TotalPrice will be held constant.
  • If you do not specify UnitPrice, TotalPrice is required. If you specify Discount and Quantity, TotalPrice or UnitPrice is required. When updating these records, you can change either TotalPrice or the UnitPrice, but not both at the same time.
  • TotalPrice is nillable, but you can’t set both TotalPrice and UnitPrice to null in the same update request. To insert the TotalPrice via the API (given only a unit price and the quantity), calculate TotalPrice as the unit price multiplied by the quantity. TotalPrice is read-only if the opportunity line item has a revenue schedule. If the opportunity line item does not have a schedule or only has quantity schedule, TotalPrice can be updated.
JimRaeJimRae

@Elke, 

Did you ever resolve this?  I am running to a similar issue, the user creates quote line item with a 100% discount.

when the record sync's back to the opportunity, the Total Price field shows as 0.00, and the Sales Price (unit Price) is blank.  If the user tries to update the OLI they get a UNIT PRICE CANNOT BE BLANK error.

AndrewTaylorAndrewTaylor

I'm curious if you ever found a solution as well.

Robert PierceRobert Pierce
I've found that while doing an Insert, that you must not provide TotalPrice, but you must provide UnitPrice.  While doing an update, you provide both.  This is through the SOAP API, so other methods may have different results.
Akhil KodakandlaAkhil Kodakandla
I'm curious if you ever found a solution as well.