Conga Product Documentation

Welcome to the new doc site. Some of your old bookmarks will no longer work. Please use the search bar to find your desired topic.

Show Page Sections

Configure the Custom Cart Approvals Preview Page

Conga Approvals includes a built-in Preview and Submit page (Apttus_Approval__PreviewSubmitApprovals) that lets a submitter review approval steps and submit a request for any context object, including agreements, opportunities, quotes, and product configurations. This standard page works without additional development.

The Custom Cart Approvals Preview page is a purpose-built Visualforce page for the CPQ shopping cart approval workflow. Use this topic when you need to display additional cart-specific information alongside the standard approval steps. Examples include product pricing summaries, discount data, and custom business fields. Because the standard Apttus_Approval__PreviewSubmitApprovals page is part of the managed Conga package and cannot be edited, creating this custom page is the only way to add custom content to the cart approval preview.

Insert custom content sections above or below the standard approval preview area by adding Visualforce markup directly to the AptsCartApprovals.page file at the positions marked in the template comments.
Note: The Custom Cart Approvals Preview page does not replace the Conga approval engine. It is a custom entry point that passes the cart context to the same managed Apttus_Approval:SObjectApprovals2 component that the standard Conga page uses. All approval routing, step evaluation, and notifications continue to work as configured in your approval process.

Comparison with the Standard Preview and Submit Page

Both pages use the same Conga approval engine and display the same approval step information. The differences are in scope, entry point, customizability, and how the cart version is resolved.

AspectStandard Conga Preview and SubmitCustom Cart Approvals Preview Page
PageApttus_Approval__PreviewSubmitApprovals. This is a managed page, part of the Conga package, and cannot be edited.AptsCartApprovals. This is a custom Visualforce page that you own and can extend.
ControllerManaged controller inside the Conga Approvals package. The source is not accessible.AptsCartApprovalsController. This is a custom Apex class where you can add your own SOQL queries and data logic.
Works withAny object configured in Approvals Custom Config (agreements, opportunities, quotes, product configurations)Specifically Apttus_Config2__ProductConfiguration__c (the CPQ shopping cart)
Entry pointButton or formula field on any object, using a URL formula: /apex/Apttus_Approval__PreviewSubmitApprovals?id={!Object.Id}Approvals button on the Quote/Proposal page layout. Always previews the most recent Saved cart version from the Configurations related list.
Cart version awarenessThe page uses only the record ID passed in the URL and does not track cart version history.The controller automatically queries the most recent Saved ProductConfiguration version. It ignores older superseded versions.
Custom content sectionsNot supported. The managed page cannot be modified.Add custom Visualforce markup above or below the approval steps in the AptsCartApprovals.page file.
Button actionsPreview, Submit, RecallReturn, Submit, Submit with Attachments, Cancel Approvals
Dialog modeNot supported natively.Supports isDialog=true, which hides the cart header and Return button for clean rendering inside a modal or iframe.
When to useStandard approvals for agreements, opportunities, or any non-cart object where no custom content is needed.Cart approvals where the preview must show cart-specific data alongside the approval steps, or where a custom page layout is required for the cart workflow.

Required Files

The following four files are required. Do not modify the utility classes. You can customize the controller and page. The steps in this topic provide sample code for each file.
FilePurpose
AptsCPQApprovalsConstants.clsShared URL parameter and status string constants. The controller extends this class. Do not modify.
AptsCPQApprovalsUtil.clsNull and empty check helpers used by the controller. Do not modify.
AptsCartApprovalsController.clsCustom Apex controller. Resolves the cart version, builds the approval context, and handles all button actions. Extend this file with your own logic.
AptsCartApprovals.pageCustom Visualforce page. Renders the approval preview and buttons. Insert your custom content sections here.

Step 1: Create the Utility Classes

Complete the following prerequisites before you begin:

  • The Conga Approvals and CPQ packages are installed in your Salesforce org.
  • The standard Conga Approvals workflow is configured for the Apttus_Config2__ProductConfiguration__c object. This includes approval processes, approval steps, entry criteria, and status picklist values.
  • An Approvals Custom Config record exists for Apttus_Config2__ProductConfiguration__c in Setup > Develop > Custom Settings.
  • You have administrator access in Salesforce.
  • You are familiar with Salesforce Visualforce and Apex.
These classes provide the shared foundation for the controller. Create them first and do not modify their code.
  • AptsCPQApprovalsConstants: This class centralizes every URL parameter name and approval status string used across the controller. The controller extends this class, so all constants are available to it directly.
  • AptsCPQApprovalsUtil: This class provides null and empty check helpers that the controller uses to process URL parameters safely before acting on them.
  1. Go to Setup > Develop > Apex Classes.
  2. Click New.
  3. Create the AptsCPQApprovalsConstants class.
    /**
     *
     * Apttus CPQ Approval
     * AptsCPQApprovalsConstants
     * @2013-2026 Apttus Inc. All rights reserved.
     */
    public virtual class AptsCPQApprovalsConstants {
        
        // approvals related params
        public static final String PARAM_RET_ID = 'retId';
        public static final String PARAM_ACTION = 'action';
        public static final String PARAM_RET_URL = 'retURL';
        public static final String PARAM_RETURNID = 'returnId';
        public static final String PARAM_RET_PAGE = 'retPage';
        public static final String PARAM_QUOTE_ID = 'quoteId';
        public static final String PARAM_IS_DIALOG = 'isDialog';
        public static final String PARAM_CONFIG_ID = 'Id';
        public static final String PARAM_SOBJECTID = 'sObjectId';
        public static final String PARAM_SOBJECTTYPE = 'sObjectType';
        public static final String PARAM_SHOW_HEADER = 'showHeader';
        public static final String PARAM_SHOW_SIDEBAR = 'showSidebar';
        public static final String PARAM_LINEITEM_IDS = 'lineItemIds';
        public static final String PARAM_ACTION_TAKEN = 'actionTaken';
        public static final String PARAM_APPROVAL_TYPE = 'approvalType';
        public static final String PARAM_FINALIZE_CLASS = 'finalizeClass';
        public static final String PARAM_AR_FIELDSET_NAME = 'arFieldSetName';
        public static final String PARAM_CONFIG_REQUEST_ID = 'configRequestId';
        public static final String PARAM_RETURN_BUTTON_LABEL = 'returnButtonLabel';
        public static final String PARAM_INCLUDE_HEADER_APPROVALS = 'includeHeaderApprovals';
        
        // actions
        public static final String ACTION_SHOW = 'show';
        public static final String ACTION_CANCEL = 'cancel';
        public static final String ACTION_SUBMIT = 'submit';
        public static final String ACTION_PREVIEW = 'preview';
        public static final String ACTION_SUBMIT_WITH_ATTACHMENTS ='submitWithAttachments';
        public static final String ENABLE_ATTACHMENTS = 'enableAttachments';
        
        // approval status
        public static final String STATUS_NONE = 'None';
        public static final String STATUS_NOT_SUBMITTED = 'Not Submitted';
        public static final String STATUS_PENDING_APPROVAL = 'Pending Approval';
        public static final String STATUS_APPROVAL_REQUIRED = 'Approval Required';
        
        // cpq-config status
        public static final String STATUS_SAVED = 'Saved';
        public static final String STATUS_FINALIZED = 'Finalized';
        public static final String STATUS_READY_FOR_FINALIZATION = 'Ready For Finalization';
        public static final String SOBJECT_CART_HEADER ='Apttus_Config2__ProductConfiguration__c';
        public static final String QUOTE_CONFIG_PAGE ='Apttus_QPConfig__ProposalConfiguration';
        public static final String USE_ADVANCED_APPROVAL = 'useAdvancedApproval';
        public static final String PREVIEW_PENDING = 'Pending';
        public static final String PREVIEW_COMPLETE = 'Complete';
    }
  4. Click Save. The system saves the class and returns to the Apex classes list.
  5. Create the AptsCPQApprovalsUtil class by clicking New and pasting the following code.
    /**
     *
     * Apttus CPQ Approvals Management
     * AptsCPQApprovalsUtil
     * @2013-2026 Apttus Inc. All rights reserved.
     */
    public with sharing class AptsCPQApprovalsUtil {
        /**
         * Checks if the given string value is null or empty.
    	 * @param strValue the string to check
         * @return <code>true</code> if the string value is null or empty,<code>false</code> otherwise
         */
        public static Boolean nullOrEmpty(String strValue) {
            // check if null or zero length string
            return (strValue == null || strValue.trim().length() == 0);
        }
        
        /**
         * Checks if the given list of objects is null or empty.
         * @param objList the list of objects to check
         * @return <code>true</code> if the list is null or empty, <code>false</code> otherwise
         */
        
        public static Boolean nullOrEmpty(List<Object> objList) {
            // check if null or empty
            return (objList == null || objList.isEmpty());
        }
    }
    
  6. Click Save.
    Warning: Both utility classes must be saved and active before you proceed. The controller does not compile without them

Step 2: Create the AptsCartApprovalsController Class

This controller manages all page behavior. It determines whether the page opened from a quote or from the cart, queries the most recent product configuration version, and builds the approval context passed to the page component.

Before you customize the controller:

  • To show extra columns beyond the defaults (Sequence, Step Name, Assigned To), pass a field set name as the arFieldSetName URL parameter when configuring the Approvals button. The field set must exist on Apttus_Config2__ProductConfiguration__c.
  • When the page opens from a quote (quoteId is set in the URL), the controller sets fromQuote to true, hides the cart header, and applies the Return label to the Return button. When the page opens from the cart (configId is set), the cart header is visible and labeled Return to Cart.
  • Add custom SOQL queries or data transformations after the prepareContext() call in the constructor.
  1. Go to Setup > Develop > Apex Classes.
  2. Click New.
  3. Pass the following code. Confirm the class name is AptsCartApprovalsController.
    /**
     *
     * Apttus CPQ Approval
     * AptsCartApprovalsController
     * @2013-2026 Apttus Inc. All rights reserved.
     */
    public with sharing class AptsCartApprovalsController extends AptsCPQApprovalsConstants {
        
        // header approval context type
        private static final String SOBJECT_CART_HEADER = 'Apttus_Config2__ProductConfiguration__c';
        private static final String QUOTE_CONFIG_PAGE = 'Apttus_QPConfig__ProposalConfiguration';
        private static final String USE_ADVANCED_APPROVAL = 'useAdvancedApproval';
        private static final String PREVIEW_PENDING = 'Pending';
        private static final String PREVIEW_COMPLETE = 'Complete';
        private static final String ACTION_SUBMIT = 'submit';
        private static final String ACTION_SUBMIT_WITH_ATTACHMENTS ='submitWithAttachments';
        private static final String ACTION_CANCEL = 'cancel';
        private static final String ENABLE_ATTACHMENTS = 'enableAttachments';
        
        // invoked from quote view?
        protected Boolean fromQuote = false;
        protected ID quoteId = null;
        
        // return button label
        protected String returnButtonLabel = null;
        
        // product configuration identifier
        private ID configId = null;
        private ID configRequestId = null;
        
        // single context
        private ID lineItemId = null;
        
        // return id
        private ID returnId = null;
        
        // return page
        private String returnPage = null;
        
        // show cart header
        private Boolean showCartHeader = true;
        private Boolean isDialog = false;
        
        // show header & sibar ?
        protected Boolean showHeader = false;
        protected Boolean showSidebar = false;
        
        // approval request fieldset name
        protected String arFieldSetName = null;
        
        // componenet params
        protected Apttus_Approval.SObjectApprovalContextParam2 ctxParam2 = null;
        
        // context header
        private Apttus_Config2__ProductConfiguration__c configSO = null;
        
        // temp object to capture actionTaken
        private Apttus_Config2__TempObject__c actionTakenObj = null;
        
        // user initiated action (submit / cancel / return)
        private String actionTaken = null;
        
        /**
         * Class Constructor
         * @param stdController the standard controller
         */
        public AptsCartApprovalsController() {
            
            // show header
            String showHeaderStr = ApexPages.currentPage().getParameters().get(PARAM_SHOW_HEADER);
            if (! AptsCPQApprovalsUtil.nullOrEmpty(showHeaderStr)) {
                showHeader = Boolean.valueOf(String.escapeSingleQuotes(showHeaderStr));
            }
            
            // show sidebar
            String showSidebarStr = ApexPages.currentPage().getParameters().get(PARAM_SHOW_SIDEBAR);
            if (! AptsCPQApprovalsUtil.nullOrEmpty(showSidebarStr)) {
                showSidebar = Boolean.valueOf(String.escapeSingleQuotes(showSidebarStr));
            }
            
            // approval request fieldset name?
            String arFieldSetNameStr = ApexPages.currentPage().getParameters().get(PARAM_AR_FIELDSET_NAME);
            if (arFieldSetNameStr != null) {
                arFieldSetName = String.escapeSingleQuotes(arFieldSetNameStr);
            }
            
            // return/exit button label shown inside the component
            String returnButtonLabelStr = ApexPages.currentPage().getParameters().get(PARAM_RETURN_BUTTON_LABEL);
            if (returnButtonLabelStr != null) {
                returnButtonLabel = String.escapeSingleQuotes(returnButtonLabelStr);
            }
            
            // quote id
            String quoteIdStr = ApexPages.currentPage().getParameters().get(PARAM_QUOTE_ID);
            if (! AptsCPQApprovalsUtil.nullOrEmpty(quoteIdStr)) {
                quoteId = String.escapeSingleQuotes(quoteIdStr);
            }
            
            // cart id
            String configIdStr = ApexPages.currentPage().getParameters().get(PARAM_CONFIG_ID);
            if (! AptsCPQApprovalsUtil.nullOrEmpty(configIdStr)) {
                configId = String.escapeSingleQuotes(configIdStr);
            }
            
            // return id
            String returnIdStr = ApexPages.currentPage().getParameters().get(PARAM_RETURNID);
            if (! AptsCPQApprovalsUtil.nullOrEmpty(returnIdStr)) {
                this.returnId = ID.valueOf(String.escapeSingleQuotes(returnIdStr));
            }
            
            // return page
            String returnPageStr = ApexPages.currentPage().getParameters().get(PARAM_RET_PAGE);
            if (! AptsCPQApprovalsUtil.nullOrEmpty(returnPageStr)) {
                this.returnPage = String.escapeSingleQuotes(returnPageStr);
            }
            
            if (quoteId != null) { // from quote
                // return label
                fromQuote = true;
                showCartHeader = false;
                if (returnButtonLabel == null) {
                    returnButtonLabel = Label.Apttus_Config2.Return;
                }
            } else if (configId != null){ // from cart
                
                // config request id
                String configRequestIdStr = ApexPages.currentPage().getParameters().get(PARAM_CONFIG_REQUEST_ID);
                if (configRequestIdStr != null) {
                    configRequestId = String.escapeSingleQuotes(configRequestIdStr);
                }
                
                // default return button label
                if (returnButtonLabel == null) {
                    returnButtonLabel = Label.Apttus_Config2.ReturnToCart;
                }
                
                // get selected line item id
                String lineItemIdStr = ApexPages.currentPage().getParameters().get(PARAM_LINEITEM_IDS);
                if (lineItemIdStr != null) {
                    lineItemId = String.escapeSingleQuotes(lineItemIdStr);
                }
                
                // include header approvals?
                String showCartHeaderStr = ApexPages.currentPage().getParameters().get(PARAM_INCLUDE_HEADER_APPROVALS);
                if (! AptsCPQApprovalsUtil.nullOrEmpty(showCartHeaderStr)) {
                    showCartHeader = Boolean.valueOf(String.escapeSingleQuotes(showCartHeaderStr));
                }   
            }
            
            // are we within a iframe?
            String isDialogStr = ApexPages.currentPage().getParameters().get(PARAM_IS_DIALOG);
            if (! AptsCPQApprovalsUtil.nullOrEmpty(isDialogStr)) {
                isDialog = Boolean.valueOf(String.escapeSingleQuotes(isDialogStr));
            }
            
            // get related quote id & preview status
            List<Apttus_Config2__ProductConfiguration__c> configSOList = null;
            configSOList = [select Id, Apttus_Config2__BusinessObjectId__c
                            , Apttus_CQApprov__Approval_Preview_Status__c
                            , Apttus_CQApprov__Approval_Status__c
                            from Apttus_Config2__ProductConfiguration__c
                            where Id = :configId];
            
            if ( ! AptsCPQApprovalsUtil.nullOrEmpty(configSOList)) {
                configSO = configSOList[0];
                quoteId = configSO.Apttus_Config2__BusinessObjectId__c;
            }
            
            // prepare context
            prepareContext();
            // LOAD ANY ADDITIONAL DATA FOR DISPLAY.......
        }
        
        /**
         * Gets context param object for component
         */
        private void prepareContext() {
            
            // prepare param
            ctxParam2 = new Apttus_Approval.SObjectApprovalContextParam2();
            ctxParam2.ctxSObjectType = SOBJECT_CART_HEADER;
            ctxParam2.ctxSObjectId = configSO.Id;
            
            // single context?
            if (lineItemId != null) {
                ctxParam2.ctxChildSObjectId = lineItemId;
            }
            
            ctxParam2.ctxApprovalStatus = configSO.Apttus_CQApprov__Approval_Status__c;
            
            // auto-preview?
            if (PREVIEW_PENDING == configSO.Apttus_CQApprov__Approval_Preview_Status__c) {
                    ctxParam2.autoPreviewIndicator = true;
            }
            
            ctxParam2.returnButtonLabel = returnButtonLabel;
            ctxParam2.processingMode = Apttus_Approval.SObjectApprovalContextParam2.PROCESSING_MODE_PREVIEW;
            ctxParam2.inDialogMode = isDialog;
            
            if (arFieldSetName != null) {
                ctxParam2.dspFieldSetName = arFieldSetName;
            }
            
            // hide cart header and return button if approvals are being reviewed within a dialog
            if (isDialog) {
                showCartHeader = false;
                ctxParam2.returnButtonLabel = null;
            }
        }
        
        /**
         * Hide buttons
         */
        public Boolean getHideButtons() {
            return (ctxParam2.ctxChildSObjectId != null);
        }
        
        /**
         * Get appropriate label based on caller
         */
        public String getReturnLabel() {
            return returnButtonLabel;
        }
        
        /*
         * Get approval request fieldset name
         */
        public String getFieldSetName() {
            return arFieldSetName;
        }
        
        /**
         * Gets show header indicator
         */
        public Boolean getShowHeader() {
            return showHeader;
        }
        
        /**
         * Gets show sidebar indicator
         */
        public Boolean getShowSidebar() {
            return showSidebar;
        }
        
        /**
         * Gets context param object for component
         */
        public Apttus_Approval.SObjectApprovalContextParam2 getContextInfo() {
            return ctxParam2;
        }
        
        /**
         * Sets contex param object
         * @param ctxInfo
         */
        public void setContextInfo(Apttus_Approval.SObjectApprovalContextParam info) {
            
        }
        
        /**
         * Can cancel?
         */
        public Boolean getCanCancel() {
            return (ctxParam2.ctxApprovalStatus == STATUS_PENDING_APPROVAL);
        }
        
        /*
         * Can submit?
         */
        public Boolean getCanSubmit() {
            return ((ctxParam2.ctxApprovalStatus == STATUS_APPROVAL_REQUIRED)
                    || (ctxParam2.ctxApprovalStatus == STATUS_NOT_SUBMITTED));
        }
        
        /**
         * Return to Quote
         */
        public PageReference doReturn() {
            PageReference pageRef = new PageReference('/' + quoteId);
            pageRef.setRedirect(true);
            return pageRef;
        }
        
        /**
         * Initial call to submit page
         */
        public PageReference doSubmit() {
            actionTaken = ACTION_SUBMIT;
            return doReturnInternal();
        }
        
        /**
         * Initial call to submit page
         */
        public PageReference doSubmitWithAttachments() {
            actionTaken = ACTION_SUBMIT_WITH_ATTACHMENTS;
            return doReturnInternal();
        }
        
        /**
         * Cancel approvals and return
         */
        public PageReference doCancel() {
            // cancel webservice
            Apttus_Approval.ApprovalsWebService.cancelApprovals(SOBJECT_CART_HEADER,configSO.Id);
            return doReturn();
        }
        
        /**
         * Get action taken - capture in temp object Name field
         */
        public Apttus_Config2__TempObject__c getActionTakenObj () {
            if (actionTakenObj == null) {
                actionTakenObj = new Apttus_Config2__TempObject__c();
            }
            return actionTakenObj;
        }
        
        public PageReference doReturnToCaller() {
            String actionTakenStr = getActionTakenObj().Apttus_Config2__DeveloperName__c;
            if (! AptsCPQApprovalsUtil.nullOrEmpty(actionTakenStr)) {
                actionTaken = String.escapeSingleQuotes(actionTakenStr);
            }
            return doReturnInternal();
        }
        
        /**
         * Return
         */
        private PageReference doReturnInternal() {
            
            PageReference retPageRef = null;
            System.debug('ACTION-TAKEN...............' + actionTaken);
            
            if ((actionTaken == ACTION_SUBMIT) || (actionTaken == ACTION_SUBMIT_WITH_ATTACHMENTS)) {
                retPageRef = Page.Apttus_Approval__SObjectApprovals2Submit;                                       
                retPageRef.getParameters().put(PARAM_SOBJECTTYPE,SOBJECT_CART_HEADER);                                       
                retPageRef.getParameters().put(PARAM_SOBJECTID, configId);                                      
                retPageRef.getParameters().put(PARAM_RET_URL, quoteId);
                
                if (actionTaken == ACTION_SUBMIT_WITH_ATTACHMENTS) {
                    retPageRef.getParameters().put(ENABLE_ATTACHMENTS, 'true');
                }
            } else {
                if (actionTaken == ACTION_CANCEL) {
                    // reset status
                    Apttus_Config2__ProductConfiguration__c updConfigSo = new
                        Apttus_Config2__ProductConfiguration__c(Id = configId, Apttus_Config2__Status__c = STATUS_APPROVAL_REQUIRED);
                    update updConfigSO;
                }
                
                if (fromQuote){
                    retPageRef = new PageReference('/' + quoteId);
                    
                } else {
                    // instance url
                    String instanceUrl = URL.getOrgDomainURL().toExternalForm();
                    
                    // build the custom page url        
                    String pageURL = (instanceUrl != null ? instanceUrl : '');
                    
                    if (!AptsCPQApprovalsUtil.nullOrEmpty(returnPage)) {
                        // return page provided, use it
                        pageURL += '/apex/' + returnPage;
                        retPageRef = new PageReference(pageURL);
                        
                    } else if (returnId != null) {
                        // use the return id
                        retPageRef = new PageReference('/' + returnId);
                        
                    } else {
                        // return to cart
                        pageURL += '/apex/' + QUOTE_CONFIG_PAGE;
                        retPageRef = new PageReference(pageURL);
                        retPageRef.getParameters().put('id', quoteId);
                        retPageRef.getParameters().put(USE_ADVANCED_APPROVAL, 'true');
                    }
                }
            }
            retPageRef.setRedirect(true);
            return retPageRef;
        }
        
        /**
         * Gets config id
         */
        public ID getConfigId() {
            return configId;
        }
        
        /**
         * Gets config request id
         */
        public ID getConfigRequestId() {
            return configRequestId;
        }
        
        /**
         * Gets from quote indicator
         */
        public Boolean getShowCartHeader() {
            return showCartHeader;
        }
    }
    
  4. Click Save.
    The system saves the class and returns to the Apex classes list.

Step 3: Create the AptsCartApprovals Visualforce Page

This page renders the approval preview. It uses the same managed Apttus_Approval:SObjectApprovals2 component as the standard Conga preview page and wraps it with the cart header, action buttons, and your custom content sections.
Note: To add custom content, insert your Visualforce markup at the comments marked <!-- === INSERT CUSTOM CONTENT ABOVE THE APPROVAL STEPS HERE === --> and <!-- === INSERT CUSTOM CONTENT BELOW THE APPROVAL STEPS HERE === --> in the page source.
  1. Go to Setup > Develop > Pages.
  2. Click New.
  3. Pass the following code. Confirm the page name is AptsCartApprovals.
    <!--
    Apttus Approvals Management
    AptsCartApprovals
    @2013-2026 Apttus Inc. All rights reserved.
    -->
    <apex:page controller="AptsCartApprovalsController"
               sidebar="{!showSidebar}" showHeader="{!showHeader}">
        <apex:form >
            <script>
            function invokeDoReturn(actionTaken) {
                // use temp object to transfer action taken value to the controller
                // unable to send as action function param - needs rerender attr
                // if rerender is specified, url does not get redirected - stays in the same page
                document.getElementById(idActionTaken).value = actionTaken;
                returnToCaller();
            }
            </script>
            <div hidden="true">
                <apex:inputField id="idActonTaken"
                                 value="{!ActionTakenObj.Apttus_Config2__DeveloperName__c}" />
            </div>
            <script>
            var idActionTaken = "{!$Component.idActonTaken}";
            </script>
            <apex:outputPanel id="idHeaderPanel">
                <Apttus_Config2:CartHeader id="idCartHeader" cartId="{!configId}"
                                           RequestId="{!configRequestId}" rendered="{!showCartHeader}" />
            </apex:outputPanel>
            <apex:pageMessages id="errorPage" />
            <apex:outputPanel id="idApprovalsPanel">
                <Apttus_Approval:SObjectApprovals2 id="idSObjectApprovals"
                                                   contextInfoParam="{!contextInfo}" />
            </apex:outputPanel>
            <apex:pageBlock rendered="{!NOT(hideButtons)}" >
                <apex:pageBlockButtons location="bottom">
                    <apex:commandButton value="{!$Label.Apttus_Approval__Return}"
                                        action="{!doReturn}" />
                    <apex:commandButton value="{!$Label.Apttus_Approval__SubmitWithAttachments}"
                                        action="{!doSubmitWithAttachments}" rendered="{!canSubmit}" />
                    <apex:commandButton value="{!$Label.Apttus_Approval__Submit}"
                                        action="{!doSubmit}" rendered="{!canSubmit}" />
                    <apex:commandButton value="{!$Label.Apttus_Approval__CancelApprovals}"
                                        action="{!doCancel}" rendered="{!canCancel}" />
                </apex:pageBlockButtons>
            </apex:pageBlock>
            <apex:actionFunction name="returnToCaller"
                                 action="{!doReturnToCaller}" />
        </apex:form>
    </apex:page>
    
  4. Click Save.
    The system saves the page and returns to the Visualforce Pages list.
  5. Click Save.
    Note: If you change the page name, update the Cart Approval Preview Page field in Setup > Develop > Custom Settings > Approvals System Properties to match.

Step 4: Add the Approvals Button to the Quote/Proposal Page Layout

The Approvals button on the Quote/Proposal page launches the custom preview page. New installations add the button automatically. If you are upgrading, you must add it manually.

  1. Go to Setup > Object Manager > Quote/Proposal > Page Layouts.
  2. Click Edit for the relevant layout.
  3. Drag the Approvals button from the palette into the Custom Buttons section.
  4. Click Save.

Step 5: Register the Custom Page in Approvals System Properties

  1. Go to Setup > Develop > Custom Settings.
  2. Click Manage next to Approvals System Properties.
  3. Click Edit.
  4. Enter AptsCartApprovals in the Cart Approval Preview Page field.
  5. Click Save.

The custom Cart Approvals Preview page is active. When a user clicks Approvals on a quote or proposal record with a configured shopping cart, the custom page opens and displays the applicable approval steps alongside any custom content you added. Agreement, opportunity, and other object approvals continue to use the standard Conga Apttus_Approval__PreviewSubmitApprovals page without any change.