I am trying to implement a button/action to redirect directly from opportunity to related quote line editor.

However, quote line editor is no longer a VF page now. I am not even sure whether it is a lightning component or not. Is there any way I can redirect from apex code/javascript to redirect to that page?

  • Why do you think edit lines isn't a VF page? The button on my quote is a VF page: SBQQ.EditLines2 – Daniel Hoechst Jan 22 '19 at 17:48
  • @DanielHoechst I am not sure by the one you mentioned as I don't see a VF page called EditLines. But I do see one called EditQuoteLines which seems to be the old version of QLE. The page sb seems more appropriate but still missing the headbar – Lance Shi Jan 23 '19 at 2:57
  • @DanielHoechst never mind, I have used apply sb VF page and it works fine now. Thank you for that – Lance Shi Jan 23 '19 at 6:05
  • Oops, that's the right one. EditLines2 is the name of the button. – Daniel Hoechst Jan 24 '19 at 4:27

I did something similar very recently for a demo. As far as I know its a visualforce page. I created a new lightning quick action button on opportunity. The idea is to create a quote with account and opportunity and navigate them to the relevant Quote Line Editor. Specifically look for navigateToQLE to pick up the url for navigation in the lightning experience.


<aura:component controller="CSSCreateNewQuoteFromOppCtrl"
                description="CSSCreateNewQuote" implements="force:lightningQuickActionWithoutHeader,force:hasRecordId">
    <aura:attribute name="record" type="Object" />
    <aura:attribute name="oppRecord" type="Opportunity" />

    <!-- Handling it onchange of attribute instead
    on init because force:recorddata isn't fetching data onload-->
    <aura:handler name="change" value="{!v.oppRecord}"
    <force:recordData recordId="{!v.recordId}"
                      fields="Id, Name, AccountId"/>

    <!-- <div>
        <lightning:spinner aura:id="mySpinner" />
    </div> -->

    <!-- Using custom spinner to apply the relative class for the spinner-->
    <div class="slds-modal__content slds-p-around--medium slds-is-relative">
        <div class="slds-spinner_container">
            <div class="slds-spinner--brand slds-spinner slds-spinner--small" role="alert">
                <span class="slds-assistive-text">Loading...</span>
                <div class="slds-spinner__dot-a"></div>
                <div class="slds-spinner__dot-b"></div>



    onOpportunityLoad: function(component,event, helper) {
        var oppId = component.get("v.oppRecord.Id");

        if (oppId != null && oppId !== undefined) {
            console.log('oppId not null');


    fetchQuote : function(component, callback) {
        console.log('entered fetch quote');
        var action = component.get("c.createQuote");
            opp : component.get("v.oppRecord")
        action.setCallback(this, function(response) {
            var state = response.getState();
            if (state === "SUCCESS") {
                console.log('success from server');
                console.log("From server: " + response.getReturnValue());
                var quote = response.getReturnValue();
            else if (state === "ERROR") {
                console.log("Error occured from server");
    navigateToQLE :function(component,quote){
        var urlEvent = $A.get("e.force:navigateToURL");
            "url": 'https://ccrazedev-dev-ed--sbqq.na78.visual.force.com/apex/sb?scontrolCaching=1&id='+quote.Id +'#quote/le?qId='+quote.Id 


Note: This is the URL I picked from classic as lightning is masking the URL. I was navigated to back to quote on save and cancel of the QLE. I guess the page is kind of detecting the UI context, so I didn't face any issues except, I was hit with an error when trying to delete the quote immediately.I was able to delete the quote from opportunity without any issue though. I didn't dig deeper into it, but just wanted to give you a heads-up anyway. You might want to try playing around with this Url to fine tune it. Last but not least not this sure if this is the right way to do it, so use it at your own discretion.

| improve this answer | |

This Apex works for both classic and Lightning

Given a button that creates a quote and then redirects the user to the quote line editor

public PageReference saveNewQuote() {
    // was: PageReference resPg = Page.SBQQ__EditQuoteLines; pre Spring 18
    PageReference resPg = Page.SBQQ__SB;
    SBQQ__c.Quote__c q;
    try {
      q = new SBQQ_Quote__c(field1 = x, ...);
      insert q;
    catch (Exception e) {
        return null;
    return resPg;
| improve this answer | |

In a Lighting Web Component, you can import the lightning-navigation service function to redirect a User to another page in Salesforce. See more on where you can use the lightning-navigation service here. This can be used to redirect a user to the QLE.

To use the lightning-navigation service in your LWC, you must import the NavigationMixin function from the lightning-navigation service. You must extend the components base class with NavigationMixin to gain access to its APIs.

In the following example, a User clicks a button that calls the saveMethod(). This method calls an Apex Controller that has also been imported called createdNewQuote. This Apex Method takes the Opportunity record Id as a parameter, which is found using the $recordId syntax and the fact that this LWC is on an Opportunity detail page. The Apex Controller returns a newly created Quote object and it's Id. In the callback method, a url is created using the returned Quote objects record Id.

This url is then used in the NavigationMixin.Navigate method. To redirect a User to the QLE, the type must be set to standard__webPage which is used to redirect users an external URL, but works to redirect a User to a VisualForce page as well.

import { LightningElement, track, api, wire } from 'lwc';
import createNewQuote from '@salesforce/apex/apexController.createNewQuote';
import { NavigationMixin} from 'lightning/navigation';

export default class lwc_class extends NavigationMixin(LightningElement) {

  @api recordId;


    createNewQuote({recordId: '$recordId'})
         .then( result  => {

              let link = '/apex/sbqq__sb?scontrolCaching=1&id=' + result.Id 
                       + '#quote/le?qId=' + result.Id;

                   type: 'standard__webPage',
                   attributes: {
                        url: link
                   true // Replaces the current page in your browser history with the URL

        .catch((error) => {
             console.log('Error: ', error);



| improve this answer | |
  • Welcome to SFSE! Please take the tour and read How to Answer. Right now, this answer is what we call a code dump. It's a block of code with little/no explanation as to how it works or how it solves the issue at hand. Code dumps tend to generate a lot of back-and-forth in the comments like "that didn't work" and "try this instead" that make it hard for other people reaching this question from, say, a google search to apply. This answer would be improved if you were to edit your answer to explain (at least) the important bits about how this approach works. – Derek F Dec 17 '19 at 20:44

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.