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
mauricio.ramos@corpitalmauricio.ramos@corpital 

calculate value in pageblocktable outputField not saved to the DB

I have a pageblocktable with a list of OpportunityLineItems which present key fields in either input or output field, one of these is a Profit__c field which gets calculated and rerended in the table via controller method. When making changes to the line item such as the quantity or the unit price, the profit get calculated correctly and debugs show that the line item contains the correct value, BUT when i click finish button ,which navigates back to the Opportunity view page, the Profit value is not passed back to the server, instead it updates with the old value which was last saved to the DB. All other values are calculated and saved accordingly.

 

Note: If I open the page and do not add discount, then the profit is saved correctly in the DB when the finish button is clicked.

 

VF page (partial code, too long for all):

.....
<apex:pageblockButtons >
<apex:commandButton value="Finish" action="{!finish}"  />
<apex:commandButton value="Cancel" action="{!cancel}"/>
</apex:pageblockButtons>
<apex:outputPanel id="tablePnl"  >
<div id="backDropBkg" class="backdrop"></div>
<apex:messages id="msgs" />   
<apex:pageblockTable value="{!OLIs}" var="OLI" id="OLIList" onrowclick="highlightOLIrow(this);" >
    <apex:column id="DelCol" width="11%;" headerValue="Action"> 
        <apex:outputPanel style="margin-right:10px;" >
            <apex:commandButton value="Delete" action="{!del}" rerender="OppLinesContainer,tablePnl, backDropBkg"   id="Del_BTN"  >
                <apex:param name="delname" value="{!OLI.id}" assignTo="{!currOLIid}"/> 
            </apex:commandButton>
        </apex:outputPanel>    
        <apex:outputPanel style="vertical-align:middle;" >
            <apex:image value="{!URLFOR($Resource.web_icons, '/arrow-up.png')}" alt="Move Up"  title="Up" >
            <apex:actionSupport event="onclick" action="{!moveOliUP}" status="sortStatus" rerender="OLIList, tablePnl, backDropBkg" >
                <apex:param name="sortUpId" value="{!OLI.id}" assignTo="{!currOLIid}"/> 
            </apex:actionSupport>
        </apex:image>    
        <apex:image value="{!URLFOR($Resource.web_icons, '/arrow-down.png')}" alt="Move Down"  title="Down">
            <apex:actionSupport event="onclick" action="{!moveOliDOWN}" status="sortStatus" rerender="OLIList, tablePnl, backDropBkg" >
                <apex:param name="sortDownId" value="{!OLI.id}" assignTo="{!currOLIid}"/> 
            </apex:actionSupport>
        </apex:image>
        </apex:outputPanel> 
    </apex:column>            
    <apex:column headerValue="Line type" id="ltcol" width="6%;">
        <apex:outputField value="{!OLI.Product_Type__c}" />     
    </apex:column>
    <apex:column headerValue="Product Name" id="prodCol" width="14%;" >
        <apex:outputText value="{!OLI.PriceBookEntry.Product2.Name}"/> 
    </apex:column>
    <apex:column headerValue="Description" width="25%"  >
        <apex:inputField value="{!OLI.Description}" id="fDescr" style="width:95%;" >
        </apex:inputField>
    </apex:column>
    <apex:column headerValue="Quantity" width="4%;" > 
    <apex:inputField value="{!OLI.Quantity_Temp__c}"  id="fQTY"  required="false" rendered="{!OLI.Product_Type__c != 'Text'}">
        <apex:actionSupport event="onchange" rerender="msgs,tablePanel,fUnitCost,fProfit,fTotalPrice,fDiscPct,fDiscAmt,fQTY" action="{!calculatePricing_ResetDiscount}" >
            <apex:param name="currOLIQty" value="{!OLI.Id}" assignTo="{!currOLIid}"/>
        </apex:actionSupport>
    </apex:inputField>       
    </apex:column>             
<apex:column headerValue="Unit Cost" width="5%;"  >
<apex:inputField value="{!OLI.Unit_cost_2__c}" id="fUnitCost"   rendered="{!OLI.Product_Type__c != 'Text'}">
    <apex:actionSupport event="onchange" rerender="msgs,tablePanel,fUnitCost,fProfit,fTotalPrice,fDiscPct,fDiscAmt" action="{!updateOLI_Pricing}" >
        <apex:param name="currOLIUC" value="{!OLI.Id}" assignTo="{!currOLIid}"/>       
    </apex:actionSupport>
</apex:inputField> 
</apex:column>
<apex:column headerValue="Sales Price" width="6%;">
    <apex:inputField value="{!OLI.SalesPrice_Temp__c}" required="true" id="fSalesPrice" rendered="{!OLI.Product_Type__c != 'Text'}">
        <apex:actionSupport event="onchange" rerender="msgs,tablePanel,fUnitCost,fProfit,fTotalPrice,fDiscPct,fDiscAmt,fSalesPrice" action="{!calculatePricing_ResetDiscount}" >
            <apex:param name="currOLISP" value="{!OLI.Id}" assignTo="{!currOLIid}"/>           
        </apex:actionSupport>
    </apex:inputField>
</apex:column>
<apex:column headerValue="Line Disc. Pct." width="6%;">
    <apex:inputField value="{!OLI.Discount_Temp__c}" id="fDiscPct" rendered="{!OLI.Product_Type__c != 'Text'}">
        <apex:actionSupport event="onchange" rerender="msgs, fUnitCost,fProfit,fTotalPrice,fDiscPct,fDiscAmt" action="{!calculateOLIDiscountAmt}" >
            <apex:param name="currOLIQty" value="{!OLI.Id}" assignTo="{!currOLIid}"/>
        </apex:actionSupport>
    </apex:inputField>
</apex:column>
<apex:column headerValue="Line Disc." width="6%;">
    <apex:inputField value="{!OLI.Line_Discount_Amount__c}" id="fDiscAmt"rendered="{!OLI.Product_Type__c != 'Text'}">
        <apex:actionSupport event="onchange" rerender="msgs, fUnitCost,fProfit,fTotalPrice,fDiscPct,fDiscAmt" action="{!calculateOLIDiscountPct}" >
            <apex:param name="currOLIAmt" value="{!OLI.Id}" assignTo="{!currOLIid}"/>
        </apex:actionSupport>
    </apex:inputField>
</apex:column>  
<apex:column headerValue="Line Profit" style="text-align:center;" width="5%;">
    <apex:outputField value="{!OLI.Profit__c}" id="fProfit"  style="width:95%" rendered="{!OLI.Product_Type__c != 'Text'}"/>       
</apex:column>
<apex:column headerValue="Line Amount" style="text-align:center;" width="12%;">
    <apex:outputPanel id="fTotalPrice">
        <apex:outputField value="{!OLI.Total_Price_Temp__c}" rendered="{!OLI.Product_Type__c != 'Text'}" />
    </apex:outputPanel>
</apex:column>         
</apex:pageBlockTable>
</apex:outputPanel>
.....

 Controller code:

private List<OpportunityLineItem> OLIs;

//currently selected Opp Line Item(oli)
public OpportunityLineItem currOLI {get;set;}
public List<OpportunityLineItem> getOLIs (){ return OLIs; }

    //CURRENTLY SELECTED OLI ID ( sets the currently selected opportunity Product)
    public String currOLIId { 
         get{ return currOLI.id; }
         set{
             currOLI  = null;
             for(OpportunityLineItem oli:OLIs) {
                 if(oli.id == value) currOLI = oli;
             }  
         } 
     }

	 //method called by Finish button to save all Opp Lines and redirect to Opp view page
    public PageReference finish() {
        if(OLIs.size()>0){
            for(OpportunityLineItem oli:OLIs){ 
            }
            try{ 
                 saveOLIs(); 
            }catch (exception e) {
              ApexPages.Message myMsg = new ApexPages.Message(ApexPages.Severity.ERROR,'Error saving Products: ' + e.getMessage());
              ApexPages.addMessage(myMsg);   
            }    
        }
        PageReference olip;
        olip = new ApexPages.StandardController(mOpp).view();
        olip.setRedirect(true);
        return olip;
    }
	
	  //save currOLI to OLIs to prevent additional info from being reset on blur
    public PageReference saveOLI2List() {
        for(OpportunityLineItem oli:OLIs){
            if(oli.id == currOLI.id) {
                update currOLI;
                oli = currOLI;
            } 
        }
        return null;
    }
    
/* check all price fields and set to 0 if null (to avoid errors in calculations*/
    public OpportunityLineItem checkPricingFields(OpportunityLineItem oli) {
        //if any of the pricing fields is null, then set to 0
        if(oli.quantity_Temp__c == null || oli.quantity_Temp__c == 0) oli.quantity_Temp__c = 1;
        if(oli.SalesPrice_Temp__c == null) oli.SalesPrice_Temp__c = 0;
        if(oli.Profit__c == null) oli.Profit__c = 0;
        if(oli.Discount_Temp__c == null || oli.Discount_Temp__c < 0) oli.discount_Temp__c = 0;
        if(oli.Unit_Cost_2__c == null) oli.Unit_Cost_2__c = 0;
        if(oli.Line_Discount_Amount__c == null) oli.Line_Discount_Amount__c = 0;
        return oli;
    }
    
    public pagereference calculatePricing_ResetDiscount() {
        calculateOLIDiscountAmt ();
        // currOLI.Line_Discount_Amount__c = 0;
        //currOLI.Discount = 0;
        updateOLI_Pricing ();
        return null;
    }
    /*Used to calculate values for Total Price*/
    public PageReference updateOLI_Pricing (){
        checkPricingFields(currOLI);
        currOLI = calculateTotalPrice(currOLI);
        currOLI = calculateOLIProfit(currOLI);
        //save currOLI to OLI list
        for(OpportunityLineItem oli:OLIs){
            if(oli.id == currOLI.id) {
                oli = currOLI;
            } 
        }
        return null;
    }
    
    private OpportunityLineItem calculateTotalPrice (OpportunityLineItem oli){
        decimal subtotal = oli.quantity_Temp__c * oli.SalesPrice_Temp__c ;
        decimal totalPrice =  subtotal - oli.Line_Discount_Amount__c; 
        oli.total_Price_temp__c = totalPrice;
        return oli;
    }
    
    private OpportunityLineItem calculateOLIProfit (OpportunityLineItem pOLI) {
        Decimal cost = pOLI.Unit_Cost_2__c ; 
        if(pOLI.Line_Discount_Amount__c != null || pOLI.Line_Discount_Amount__c != 0 ) 
            pOLI.Profit__c = (pOLI.Quantity_Temp__c * pOLI.SalesPrice_Temp__c - pOLI.Line_Discount_Amount__c ) - (pOLI.Quantity_Temp__c * cost);
        else
             pOLI.Profit__c = (pOLI.Quantity_Temp__c * pOLI.SalesPrice_Temp__c) - (pOLI.Quantity_Temp__c * cost);
        return pOLI;
    }

    public PageReference calculateOLIDiscountPct (){
        checkPricingFields(currOLI);
        decimal subtotal = currOLI.Quantity_Temp__c * currOLI.SalesPrice_Temp__c;
        currOLI.Discount_Temp__c = currOLI.Line_Discount_Amount__c != 0 ? (currOLI.Line_Discount_Amount__c / subtotal ) * 100: 0 ;
        updateOLI_Pricing();        
        return null;
    }
    
    public PageReference calculateOLIDiscountAmt () {
        checkPricingFields(currOLI);
        decimal subtotal = currOLI.Quantity_Temp__c * currOLI.SalesPrice_Temp__c;
        currOLI.Line_Discount_Amount__c = subtotal != null || subtotal != 0 ? (subtotal / 100) * currOLI.Discount_Temp__c : 0;
        updateOLI_Pricing();
        return null;
    } 

    //this method is used when adding a new Opp Line from a list of PricebookEntries from the selected Pricebook of the Opp
    public PageReference addOLI() {
        try{
            /*Add a new Opp Line from pPBE*/
            if(currPBE != null){
                OpportunityLineItem oli = new OpportunityLineItem();
                if(currPBE.Product2.Product_Type__c != 'Text'){
                  //  if (checkIfProductAddedAlready(currPBEId )) return null;
                }
                oli = initOLI_withDefaults(currPBE);
                OLIs.add(oli);
                newOLIsAdded.add(oli);
                currOLI = oli; 
            }
        }catch (Exception e) {
            ApexPages.Message myMsg = new ApexPages.Message(ApexPages.Severity.ERROR,'Error adding Product to Opportunity: ' + e.getMessage());
            ApexPages.addMessage(myMsg); 
        }
        return null; 
        
    }
    
    private OpportunityLineItem initOLI_withDefaults(PricebookEntry pbe) {
            OpportunityLineItem oli = new OpportunityLineItem();  
            oli.Quantity_Temp__c = 1;
            oli.Quantity = 1;
            oli.PricebookEntry = pbe;
            oli.PricebookEntryId = pbe.id;
            oli.OpportunityId = mOpp.id;
            oli.UnitPrice =  pbe.UnitPrice;
            oli.SalesPrice_Temp__c = pbe.UnitPrice;
            oli.Unit_cost_2__c = getProdUnitCost(pbe);
            oli.discount_Temp__c = 0;
            oli.Discount = 0;
            oli.Total_Price_Temp__c = oli.UnitPrice;
            oli.Profit__c = oli.UnitPrice - oli.Unit_Cost_2__c;
            oli.Profit__c =  oli.UnitPrice - oli.Unit_Cost_2__c;
            oli.Description = getOLIDescription_FromAccount();
            oli.Line_Discount_Amount__c = 0;
            oli.Line_sorting__c = getNewLineSortingNumber(); // method not shown BUT working as expected
            insert oli;
            oli = loadOLI(oli);
            return oli;
    }
	
    public PageReference saveOLIs() {
        try{
            if(OLIs.size()>0){
                //update each OLI price fields wih temp pricing fields:
                for(opportunityLineItem oli:OLIs){
                    oli.quantity = oli.quantity_temp__c;
                    oli.UnitPrice = oli.SalesPrice_temp__c;
                    oli.Discount = oli.Discount_Temp__c;
                    //oli.Profit__c = oli.Profit__c; 
                }
                //save all olis in list
                upsert OLIs;
            }
        }catch(Exception e){
            ApexPages.Message myMsg = new ApexPages.Message(ApexPages.Severity.ERROR,'Error saving Opportunity Line Items: ' + e.getMessage());
            ApexPages.addMessage(myMsg);        
        }
        return null;
    }
	
	private void loadOLIs(){
	   OLIs = [Select id, ...and all the needed fields From OpportunityLineItem Where OpportunityId = :mOpp.Id Order by Line_Sorting__c ASC];
	   for(opportunityLineItem o:OLIs){
		   o.Total_Price_Temp__c = o.TotalPrice;
		   o.Quantity_Temp__c = o.Quantity;
		   o.SalesPrice_Temp__c  = o.UnitPrice;
		   o.Discount_Temp__c = o.Discount;
    }
	 
  

 Only relevant code is shown since all other code is for other purposes, such as searching for products, etc. Also removed all debug lines to shorten the amount of code submitted. To note is that I set  standard field values such as Quantity, UnitPrice and Discount all together at the end since from temporary fields.