7

Building a wizard to mirror the functionality of the opportunity products wizard built in (minus an otherwise unmovable field). The first page, where the user can select from a list of products works fine, the controller (as far as I can tell) works fine. The unit tests all passed. But the second page, which is supposed to show a repeated section of fields displays nothing, even when the list containing all the items is populated. Does anyone know what is wrong?

Controller:

public class OpportunityProductsController {

// PROPERTIES
public List<SelectableListProduct> wrappedList { get; set; }
public List<OpportunityLineItem> itemsToAdd { get; set; }
public Opportunity controllingOpp { get; set; }

// CONSTRUCTOR
public OpportunityProductsController() {
    //Init Id
    try {
        controllingOpp = [SELECT Id FROM Opportunity WHERE Id=:ApexPages.currentPage().getParameters().get('oid') LIMIT 1];
    } catch (System.QueryException e) {
        controllingOpp = new Opportunity();
    }

    //Initialise lists.
    wrappedList = new List<SelectableListProduct>();
    itemsToAdd = new List<OpportunityLineItem>();

    //Get a list of pricebook entries.
    List<PricebookEntry> unwrappedEntries = new List<PricebookEntry>();
    unwrappedEntries = [SELECT Id, 
                               Product2.Name,
                               Product2.ProductCode,
                               Product2.Standard_Install_Cost__c,
                               Product2.Standard_Monthly_Cost__c,
                               Product2.Supplier__c
                               FROM PricebookEntry WHERE Product2.IsActive = true];

    wrappedList = PopulateWrappedList(unwrappedEntries);

}

List<SelectableListProduct> PopulateWrappedList(List<PricebookEntry> unwrapped) {

    List<SelectableListProduct> returnList = new List<SelectableListProduct>();

    //For each record returned, created a wrapped object and put it in the list.
    for (PricebookEntry entry : unwrapped) {
        SelectableListProduct newSLP = new SelectableListProduct(entry, false);
        returnList.add(newSLP);
    }

    //Sort the list.
    returnList.sort();
    return returnList;
}

List<OpportunityLineItem> PopulateSelectedList(List<SelectableListProduct> wrapped) {

    List<OpportunityLineItem> returnList = new List<OpportunityLineItem>();

    for (SelectableListProduct entry : wrapped) {
        if (entry.isSelected) {
            OpportunityLineItem newOLI = new OpportunityLineItem();
            newOLI.OpportunityId = controllingOpp.Id;
            newOLI.PricebookEntryId = entry.cProduct.Id;
            newOLI.UnitPrice = 0;
            newOLI.Install_Cost__c = entry.cProduct.Product2.Standard_Install_Cost__c;
            newOLI.Monthly_Cost__c = entry.cProduct.Product2.Standard_Monthly_Cost__c;
            returnList.add(newOLI);
        }
    }

    returnList.sort();
    return returnList;
}

public PageReference Step1() {
    return Page.SelectNewItems;
}

public PageReference Step2() {
    //populate selected products list
    itemsToAdd = PopulateSelectedList(wrappedList);
    return Page.EditNewItems;
}

public PageReference Save() {
    //Attempt to save items.
    try {
        insert itemsToAdd;
    } catch (System.DMLException e) {
        ApexPages.addMessages(e);
        return null;
    }
    //Redir to opp detail page.
    PageReference opportunityPage = new ApexPages.StandardController(controllingOpp).view();
    opportunityPage.setRedirect(true);
    return opportunityPage;
}

public PageReference Cancel() {
    PageReference opportunityPage = new ApexPages.StandardController(controllingOpp).view();
    opportunityPage.setRedirect(true);        
    return opportunityPage;
}    
}

The first visualforce page (works fine):

<apex:page controller="OpportunityProductsController" tabstyle="Opportunity">
<apex:sectionHeader title="Add New Opportunity Products" subtitle="Select Products"/>
<apex:form >
  <apex:pageBlock title="Product List">
     <apex:pageBlockButtons >
         <apex:commandButton action="{!Step2}" value="Next" immediate="true"/>
         <apex:commandButton action="{!Cancel}" value="Cancel"/>
     </apex:pageBlockButtons>
     <!-- PRODUCT LIST -->
     <apex:pageBlockTable value="{!wrappedList}" var="SelectableListProduct">
        <apex:column >
           <apex:inputCheckbox value="{!SelectableListProduct.isSelected}"/>
        </apex:column>
        <apex:column value="{!SelectableListProduct.cProduct.Product2.Name}"/>
        <apex:column value="{!SelectableListProduct.cProduct.Product2.ProductCode}"/>
        <apex:column value="{!SelectableListProduct.cProduct.Product2.Standard_Install_Cost__c}"/>
        <apex:column value="{!SelectableListProduct.cProduct.Product2.Standard_Monthly_Cost__c}"/>
        <apex:column value="{!SelectableListProduct.cProduct.Product2.Supplier__c}"/>
     </apex:pageBlockTable>

  </apex:pageBlock>
  </apex:form>
  </apex:page>

The second visualforce page (displays buttons and nothing else):

<apex:page controller="OpportunityProductsController" tabStyle="Opportunity">
<apex:sectionHeader title="Add New Opportunity Products" subtitle="Edit Products"/>
<apex:form >
    <apex:pageBlock title="Edit Item Information">
        <apex:pageBlockButtons >
            <apex:commandButton action="{!Save}" value="Done"/>
            <apex:commandButton action="{!Step1}" value="Back"/>
            <apex:commandButton action="{!Cancel}" value="Cancel"/>
        </apex:pageBlockButtons>
            <apex:outputField value="{!controllingOpp.Name}"/>
            <apex:repeat value="{!itemsToAdd}" var="currentItem">
                <apex:outputField value="{!currentItem.PricebookEntry.Name}" />
                <apex:inputField value="{!currentItem.Install_Cost__c}" />
                <apex:inputField value="{!currentItem.Monthly_Cost__c}" />
            </apex:repeat>
    </apex:pageBlock>
</apex:form>
</apex:page>

EDIT: Here's selectable list product:

public class SelectableListProduct implements Comparable
{
 public Boolean isSelected {get;set;}
 public PricebookEntry cProduct {get;set;}

 public SelectableListProduct(PricebookEntry cProduct, Boolean selected){
      this.cProduct = cProduct;
      this.isSelected = selected;
 }

 public Integer compareTo(Object comparisonObject)
 {
     SelectableListProduct comparisonProduct = (SelectableListProduct)comparisonObject;
     return cProduct.Product2.Name.compareTo(comparisonProduct.cProduct.Product2.Name);
 }
}
7
  • I assume you are checking some checkboxes on the first page? Could you post the SelectableListProduct class too?
    – Keith C
    Commented Apr 15, 2014 at 13:59
  • Added to post. On the first page I render a table, the "value" attribute of each checkbox is set to the selectableListProduct's isSelected property. When I navigate to the next page, do I need to perform any manual checks? When going from page one to page two, I add the cProduct of all SLPs with isSelected set to true to a list of Opportunity Line Items. Is there something else I need to do?
    – Adam
    Commented Apr 15, 2014 at 14:04
  • No the reason I asked for SelectableListProduct is because I couldn't see anything wrong in the code you posted and I can't see anything wrong in SelectableListProduct either. Someone else may be more observant, but otherwise all I can suggest is to sprinkle System.debug around your controller.
    – Keith C
    Commented Apr 15, 2014 at 14:20
  • If I use System.debug() outside of a test class, where can I find the debug logs?
    – Adam
    Commented Apr 15, 2014 at 14:56
  • One way is Setup -> Logs -> Debug Logs and add the User you are testing under as a "Monitored User". Refresh that page after running your wizard and you should see a log entry per page.
    – Keith C
    Commented Apr 15, 2014 at 14:58

2 Answers 2

1

In the method Step2(), you're not setting your URL Parameter.

public PageReference Step2() {
    //populate selected products list
    itemsToAdd = PopulateSelectedList(wrappedList);
    return new PageReference('/apex/EditNewItem?oid='+controllingOpp.id);
}

Update

I was figuring itemsToAdd wasn't getting populated because it wasn't pulling info from anywhere. itemsToAdd isn't getting populated. Notice how there's nothing populating the records when the controller is initialized again going to the second page? All you do is create a new List<OpportunityLineItem> in the constructor. (In your Step2() method, you are calling PopulateSelectdList() but that information is never sent to the controller for Page 2.)

I see two options:

  1. Pass sufficient information via URL Parameter
  2. Call a method to initialize itemsToAdd on the 2nd Page via <apex:page ... action="{!PopulateItemsToAdd}" /> / Initialize itemsToAdd in the controller
5
  • That is a good point, but it doesn't seem to be the root of the problem.
    – Adam
    Commented Apr 15, 2014 at 15:28
  • check out my updated answer Commented Apr 15, 2014 at 16:22
  • From a brief test I did the constructor only runs as part of the first page being rendered. Presumably for subsequent pages, the object is recreated from the view state without executing any code. (But for the several wizards I have done, I have not had code in the constructor.)
    – Keith C
    Commented Apr 15, 2014 at 17:15
  • all i can add is if the data isnt showing up, then it's probably not being set. Commented Apr 15, 2014 at 22:02
  • The data is being sent, I've checked using the developer footer. If I initialise all the wrapper classes with their isSelected value to true instead of false, all of them appear on the next page. I've found the root of the problem in the fact that the checkboxes do not change the value of isSelected, even though they should.
    – Adam
    Commented Apr 17, 2014 at 8:26
1

change [opportunityPage.setRedirect(true)] to [opportunityPage.setRedirect(false)],you can get the data.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .