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
ckellieckellie 

How to change Quote Line Item sort order

I am trying to write a trigger to chang the quotelineitem's sort order by copying the item number, which is a custom field. I am recieving the following error:

 

Compile Error: Field is not writeable: QuoteLineItem.SortOrder

 

Here is my code:

trigger LineItemSortOrder on QuoteLineItem (Before Insert, Before Update) {
    
    QuoteLineItem[] q = trigger.new;

   for (integer i = 0; i < q.size(); i++) {
      QuoteLineItem nquote = q[i];
      nquote.sortorder = integer.valueof(nquote.Item__c);
  
   }    
    
}

 

Is there any way around this error?

 

Thank you,

ckellie

Best Answer chosen by Admin (Salesforce Developers) 
ckellieckellie

Thank you for the link.

 

I have customized the script as needed and habe adeded the script to the page layou via apex button. I have customized the button to order the Quote Item number, Unfortunately the button is not doing anything. What am I doing wrong and what do I need to change?

 

Javascript button:

{!REQUIRESCRIPT("/soap/ajax/22.0/connection.js")}
{!REQUIRESCRIPT("/soap/ajax/22.0/apex.js")}

var oppID = '{!Opportunity.Id}';

//call the apex web service to get the OLIs in the desired sort order for this opp
var result = sforce.apex.execute("customSort","MRsort",{oppID: oppID});   

//need to post a form to /oppitm/lineitemsort.jsp because this is how SFDC
//does it but there is not direct API to do the sorting (thus the awkward workaround)
var form = document.createElement("form");
form.setAttribute("method", "post");
form.setAttribute("action", "/oppitm/lineitemsort.jsp");

//set the id of the request to the opportunity ID
var hiddenField = document.createElement("input");
hiddenField.setAttribute("type", 'hidden');
hiddenField.setAttribute("name", 'id');
hiddenField.setAttribute("value", '{!Opportunity.Id}');   
form.appendChild(hiddenField);

//the name of the sorted OLI list that the JSP is expecting is "duel0"
var hiddenField = document.createElement("input");
hiddenField.setAttribute("type", 'hidden');
hiddenField.setAttribute("name", 'duel0');
hiddenField.setAttribute("value", String(result));
form.appendChild(hiddenField);

var hiddenField = document.createElement("input");
hiddenField.setAttribute("type", 'hidden');
hiddenField.setAttribute("name", 'retURL');
hiddenField.setAttribute("value", '/{!Opportunity.Id}');
form.appendChild(hiddenField);

//set to save
var hiddenField = document.createElement("input");
hiddenField.setAttribute("type", 'hidden');
hiddenField.setAttribute("name", 'save');
hiddenField.setAttribute("value", ' Save ');
form.appendChild(hiddenField);

//need to do this so it works in IE
document.body.appendChild(form);

//submit!!
form.submit();

 

Aperx Class:

global class customSort {

    webservice static String MRsort(Id oppID)
    {
        //pull back the OLIs in a specific sort order
        List<OpportunityLineItem> olis = [Select oli.Id, oli.Opportunity_Line_Item__c
                From OpportunityLineItem oli
                Where oli.OpportunityId = :oppId
                ORDER BY oli.Opportunity_Line_Item__c];
        
        //build the comma separated 15 character OLI Id string to send back
        String sortedIds = '';                    
        for(OpportunityLineItem oli : olis)
        {
            sortedIds += String.valueOf(oli.Id).substring(0,15) + ',';      
        }
        
        //remove the last comma
        sortedIds = sortedIds.substring(0,sortedIds.length() - 1);
        return sortedIds;
    }
}

 

What am I dong wrong and how do I fix it?

 

regards,

ckellie

All Answers

kpeterskpeters

See http://boards.developerforce.com/t5/Visualforce-Development/Re-opening-the-disscussion-of-SortOrder-on-OpportunityLineItem/m-p/260295#M33758 for the unfortunately complicated workaround I came up with.  It is for OpportunityLineItem but works the same for QuoteLineItem.

ckellieckellie

Thank you for the link.

 

I have customized the script as needed and habe adeded the script to the page layou via apex button. I have customized the button to order the Quote Item number, Unfortunately the button is not doing anything. What am I doing wrong and what do I need to change?

 

Javascript button:

{!REQUIRESCRIPT("/soap/ajax/22.0/connection.js")}
{!REQUIRESCRIPT("/soap/ajax/22.0/apex.js")}

var oppID = '{!Opportunity.Id}';

//call the apex web service to get the OLIs in the desired sort order for this opp
var result = sforce.apex.execute("customSort","MRsort",{oppID: oppID});   

//need to post a form to /oppitm/lineitemsort.jsp because this is how SFDC
//does it but there is not direct API to do the sorting (thus the awkward workaround)
var form = document.createElement("form");
form.setAttribute("method", "post");
form.setAttribute("action", "/oppitm/lineitemsort.jsp");

//set the id of the request to the opportunity ID
var hiddenField = document.createElement("input");
hiddenField.setAttribute("type", 'hidden');
hiddenField.setAttribute("name", 'id');
hiddenField.setAttribute("value", '{!Opportunity.Id}');   
form.appendChild(hiddenField);

//the name of the sorted OLI list that the JSP is expecting is "duel0"
var hiddenField = document.createElement("input");
hiddenField.setAttribute("type", 'hidden');
hiddenField.setAttribute("name", 'duel0');
hiddenField.setAttribute("value", String(result));
form.appendChild(hiddenField);

var hiddenField = document.createElement("input");
hiddenField.setAttribute("type", 'hidden');
hiddenField.setAttribute("name", 'retURL');
hiddenField.setAttribute("value", '/{!Opportunity.Id}');
form.appendChild(hiddenField);

//set to save
var hiddenField = document.createElement("input");
hiddenField.setAttribute("type", 'hidden');
hiddenField.setAttribute("name", 'save');
hiddenField.setAttribute("value", ' Save ');
form.appendChild(hiddenField);

//need to do this so it works in IE
document.body.appendChild(form);

//submit!!
form.submit();

 

Aperx Class:

global class customSort {

    webservice static String MRsort(Id oppID)
    {
        //pull back the OLIs in a specific sort order
        List<OpportunityLineItem> olis = [Select oli.Id, oli.Opportunity_Line_Item__c
                From OpportunityLineItem oli
                Where oli.OpportunityId = :oppId
                ORDER BY oli.Opportunity_Line_Item__c];
        
        //build the comma separated 15 character OLI Id string to send back
        String sortedIds = '';                    
        for(OpportunityLineItem oli : olis)
        {
            sortedIds += String.valueOf(oli.Id).substring(0,15) + ',';      
        }
        
        //remove the last comma
        sortedIds = sortedIds.substring(0,sortedIds.length() - 1);
        return sortedIds;
    }
}

 

What am I dong wrong and how do I fix it?

 

regards,

ckellie

This was selected as the best answer
ckellieckellie

Changed field Opportunity_Line_Item__c to Quote_Item_Number__c and it is working just fine. Thank you very much kpeters

kpeterskpeters

Good to hear ckellie!  Please mark it as the solution if it solved your problem.

Oxala AlexandreOxala Alexandre

Hi

SFDC has introduced a new parameter "_CONFIRMATIONTOKEN' to avoid your trick so here is new JS code (not very nice for parsing part) that works well :

 

{!REQUIRESCRIPT("/soap/ajax/22.0/connection.js")}
{!REQUIRESCRIPT("/soap/ajax/22.0/apex.js")}
{!REQUIRESCRIPT("https://ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js")}


// get confirmation token and set webform field
$.ajax({
url:"./oppitm/lineitemsort.jsp?id={!Opportunity.Id}",
contentType:"text/html",
xhrFields:{withCredentials:true}
}).done (
function(data){
key_start='id="_CONFIRMATIONTOKEN" value="';
key_end='"';
pos_start=data.indexOf(key_start);
pos_end=data.indexOf(key_end,pos_start+key_start.length);
confirmationtoken=data.substring(pos_start+key_start.length,pos_end);

//need to post a form to /oppitm/lineitemsort.jsp because this is how SFDC
//does it but there is not direct API to do the sorting (thus the awkward workaround)
var form = document.createElement("form");
form.setAttribute("method", "post");
form.setAttribute("action", "/oppitm/lineitemsort.jsp");

var hiddenField = document.createElement("input");
hiddenField.setAttribute("type", 'hidden');
hiddenField.setAttribute("name", '_CONFIRMATIONTOKEN');
hiddenField.setAttribute("value", confirmationtoken);
form.appendChild(hiddenField);

var oppID = '{!Opportunity.Id}';

//call the apex web service to get the OLIs in the desired sort order for this opp
var result = sforce.apex.execute("customSort","MRsort",{oppID: oppID});

//set the id of the request to the opportunity ID
var hiddenField = document.createElement("input");
hiddenField.setAttribute("type", 'hidden');
hiddenField.setAttribute("name", 'id');
hiddenField.setAttribute("value", '{!Opportunity.Id}');
form.appendChild(hiddenField);

//the name of the sorted OLI list that the JSP is expecting is "duel0"
var hiddenField = document.createElement("input");
hiddenField.setAttribute("type", 'hidden');
hiddenField.setAttribute("name", 'duel0');
hiddenField.setAttribute("value", String(result));
form.appendChild(hiddenField);

var hiddenField = document.createElement("input");
hiddenField.setAttribute("type", 'hidden');
hiddenField.setAttribute("name", 'retURL');
hiddenField.setAttribute("value", '/{!Opportunity.Id}');
form.appendChild(hiddenField);

var hiddenField = document.createElement("input");
hiddenField.setAttribute("type", 'hidden');
hiddenField.setAttribute("name", 'cancelURL');
hiddenField.setAttribute("value", '/{!Opportunity.Id}');
form.appendChild(hiddenField);

var hiddenField = document.createElement("input");
hiddenField.setAttribute("type", 'hidden');
hiddenField.setAttribute("name", 'save_new_url');
hiddenField.setAttribute("value", '/oppitm/lineitemsort.jsp');
form.appendChild(hiddenField);

//set to save
var hiddenField = document.createElement("input");
hiddenField.setAttribute("type", 'hidden');
hiddenField.setAttribute("name", 'save');
hiddenField.setAttribute("value", 'Enregistrer');
form.appendChild(hiddenField);

//need to do this so it works in IE
document.body.appendChild(form);
form.submit();
}
);

MezzaMezza
Oxala and kpeters, thanks for solving what must be one of Salesforce's most annoying bugs!
Mitesh SuraMitesh Sura
Hello! Oxala,

Thank you for alternate solution, it works great on custom JS button. However I have a custom VF page and need to call this code when user clicks "Save" button. I tried below code, but it won't work. Essentially, I think the issue is getting the confirmation token from VF page.

Thank you for your time in advance.
 
<script src="/soap/ajax/30.0/connection.js"/>
	    <script src="/soap/ajax/30.0/apex.js"/>
        
        <script type='text/javascript'> 
       	    function sort(){
		    	alert('sort');
		    	
				//var oppID = 'sObj["Id"]';
				var oppId = '006d000000PD4D4'
				//alert(oppID);
		    	
		    	// get confirmation token and set webform field
				$.ajax({
					url:"./oppitm/lineitemsort.jsp?id="+oppId,
					contentType:"text/html",
					xhrFields:{withCredentials:true}
				}).done (
					function(data){
						key_start='id="_CONFIRMATIONTOKEN" value="';
						key_end='"';
						pos_start=data.indexOf(key_start);
						pos_end=data.indexOf(key_end,pos_start+key_start.length);
						confirmationtoken=data.substring(pos_start+key_start.length,pos_end);
						
						console.log(confirmationtoken);
						
                                               //TRIED $ACTION, but that did not work too
						var URL = "{!URLFOR($Action.OpportunityLineItem.Sort,'006d000000PD4D4',[retURL='/006d000000PD4D4'])}";
						console.log(URL);
						
						//need to post a form to /oppitm/lineitemsort.jsp because this is how SFDC
						//does it but there is not direct API to do the sorting (thus the awkward workaround)
						var form = document.createElement("form");
						form.setAttribute("method", "post");
						form.setAttribute("action", "/oppitm/lineitemsort.jsp");
						//form.setAttribute("action", URL);
						
						var hiddenField = document.createElement("input");
						hiddenField.setAttribute("type", 'hidden');
						hiddenField.setAttribute("name", '_CONFIRMATIONTOKEN');
						hiddenField.setAttribute("value", confirmationtoken);
						form.appendChild(hiddenField);

                                                :
                                                :
                                                :
                                                
                                                //set to save
						var hiddenField = document.createElement("input");
						hiddenField.setAttribute("type", 'hidden');
						hiddenField.setAttribute("name", 'save');
						hiddenField.setAttribute("value", 'Enregistrer');
						form.appendChild(hiddenField);
						
						//need to do this so it works in IE
						document.body.appendChild(form);
						form.submit();
					}
				);
		    }

 
DRahlfDRahlf

Hi guys!

I've exact the same problem as Mitesh and the problem is the ajax call returning with the authentification error already, so no token to be found there. Did anybody solve this for a call out of a VF-Page? 

Merri FurlongMerri Furlong
I am stuck on trying to write a test class for the Apex Class called customSort.   My test method is saying "List controllers are not supported for OpportunityLineItem" ...    cannot deploy without a way to test this webservice.  Any help?
 
Merri FurlongMerri Furlong
in response to my own question - test class includes this section: for what it's worth. Hope this helps someone else.

       testSort  = [Select oli.Id, oli.Line_Number__c,Descnickname__c
                From OpportunityLineItem oli
                Where oli.OpportunityId = :testOpp.id
                ORDER BY oli.Line_Number__c];

        system.test.startTest();
        CustomSort controller = new CustomSort();
        PageReference pageRef = new PageReference('/'+testOpp.id);
        system.test.setCurrentPage(pageRef);
        ApexPages.CurrentPage().getparameters().put('oppid', testOpp.id);
        pageRef.getParameters().put('oppid', testOpp.id);
           
        CustomSort.MRsort(testOpp.id);
   
        System.assert(testSort != null);
       
       
        system.test.stopTest();
BoonPlusBoonPlus
The SortOrder field in the Opportunity Product and Quote Line Item objects is now updatable in Winter '20 release (API Version 47.0). I utilized this new updateable field access for the Quote Line Item Sorting in my apps, and the sorted Quote Line Items can now be populated onto a Quote PDF. Below is a video which shows how it works...

Sort Quote Line Items and Create PDF Made Easy in Salesforce

Sort Quote Line Items and Create PDF Made Easy in Salesforce