• Glen Ninan
  • 15 Points
  • Member since 2015

  • Chatter
  • 0
    Best Answers
  • 0
    Likes Received
  • 0
    Likes Given
  • 0
  • 3
Error : 
User-added image

I have overridden the add and new standard buttons as well.
User-added image
Product2Extension.cls :
public class Product2Extension {

  public List<ProductWrapper> productsToInsert {get; set;}

  public Product2Extension(ApexPages.StandardController controller){
    productsToInsert = new List<ProductWrapper>();

  public void AddRows(){
    for (Integer i=0; i<Constants.DEFAULT_ROWS; i++ ) {
      productsToInsert.add( new ProductWrapper() );

  public List<ChartHelper.ChartData> GetInventory(){
    return ChartHelper.GetInventory();

  public List<SelectOption> GetFamilyOptions() {
    List<SelectOption> options = new List<SelectOption>();
    options.add(new SelectOption(Constants.SELECT_ONE, Constants.SELECT_ONE));
    for(PickListEntry eachPicklistValue : Constants.PRODUCT_FAMILY) {
      options.add(new SelectOption(eachPicklistValue.getValue(), eachPicklistValue.getLabel()));
    return options;

  public PageReference Save(){
    Savepoint sp = Database.setSavepoint();
    try {
      List<Product2> products = new List<Product2>();
      List<PricebookEntry> pbes = new List<PricebookEntry>();

      for (ProductWrapper prodwrapper : productsToInsert) {
        if(prodwrapper.productRecord != null && prodwrapper.pricebookEntryRecord != null) {
          if(prodwrapper.productRecord.Name != null && prodwrapper.productRecord.Family != null && constants.SELECT_ONE != prodwrapper.productRecord.Family && prodwrapper.productRecord.Initial_Inventory__c != null && prodwrapper.pricebookEntryRecord.UnitPrice != null) {
            PricebookEntry pbe = prodwrapper.pricebookEntryRecord;
            pbe.IsActive = true;
            pbe.Pricebook2Id = Constants.STANDARD_PRICEBOOK_ID;

      insert products;

      for (integer i = 0; i < pbes.size(); i++) {
        pbes[i].Product2Id = products[i].Id;
      insert pbes;

      //If successful clear the list and display an informational message
      apexPages.addMessage(new ApexPages.message(ApexPages.Severity.INFO,productsToInsert.size()+' Inserted'));
      productsToInsert.clear();         //Do not remove
      AddRows();        //Do not remove
    } catch (Exception e){
      apexPages.addMessage(new ApexPages.message(ApexPages.Severity.ERROR, Constants.ERROR_MESSAGE));
    return null;

  public class ProductWrapper {
    public Product2 productRecord {get; set;}
    public PriceBookEntry pricebookEntryRecord {get; set;}

    public ProductWrapper() {
      productRecord = new product2(Initial_Inventory__c =0);
      pricebookEntryRecord = new pricebookEntry(Unitprice=0.0);
Product2New​.page :
<apex:page standardcontroller="Product2" extensions="Product2Extension">
  <apex:sectionHeader title="New Product" subtitle="Add Inventory"/>
  <apex:pageMessages id="pageMessages"/>
  <apex:form id="form">
      <apex:pageBlock title="Existing Inventory" id="existingInv">
        <apex:chart data="{!Inventory}" width="600" height="400">
          <apex:axis type="Category" fields="name" position="left" title="Product Family"/>
          <apex:axis type="Numeric" fields="val" position="bottom" title="Quantity Remaining"/>
          <apex:barSeries axis="bottom" orientation="horizontal" xField="val" yField="name"/>
      <apex:pageBlock title="New Products">
        <apex:pageBlockButtons location="top">
          <apex:commandButton action="{!save}" value="Save" reRender="existingInv, orderItemTable, pageMessages"/>
        <apex:pageBlockButtons location="bottom">
          <apex:commandButton action="{!addRows}" value="Add" reRender="orderItemTable, pageMessages"/>

        <apex:pageBlockTable value="{!productsToInsert}" var="p" id="orderItemTable">
          <apex:column headerValue="{!$ObjectType.Product2.Fields.Name.Label}">
            <apex:inputText value="{!p.productRecord.Name}"/>
          <apex:column headerValue="{!$ObjectType.Product2.Fields.Family.Label}">
            <apex:selectList value="{!p.productRecord.Family}" size="1" multiselect="false">
              <apex:selectOptions value="{!FamilyOptions}"></apex:selectOptions>
          <apex:column headerValue="{!$ObjectType.Product2.Fields.IsActive.Label}">
            <apex:inputField value="{!p.productRecord.isActive}"/>
          <apex:column headerValue="{!$ObjectType.PricebookEntry.Fields.UnitPrice.Label}">
            <apex:inputText value="{!p.pricebookEntryRecord.UnitPrice}"/>
          <apex:column headerValue="{!$ObjectType.Product2.Fields.Initial_Inventory__c.Label}">
            <apex:inputField value="{!p.productRecord.Initial_Inventory__c}"/>
product2Trigger : 
trigger product2Trigger on Product2 (after update) {
Constants.cls : 
public class Constants {
  public static final Integer DEFAULT_ROWS = 5;
  public static final String SELECT_ONE = Label.Select_One;
  public static final String INVENTORY_LEVEL_LOW = Label.Inventory_Level_Low;
  public static final List<Schema.PicklistEntry> PRODUCT_FAMILY = Product2.Family.getDescribe().getPicklistValues();
  public static final String DRAFT_ORDER_STATUS = 'Draft';
  public static final String ACTIVATED_ORDER_STATUS = 'Activated';
  public static final String INVENTORY_ANNOUNCEMENTS = 'Inventory Announcements';
  public static final String ERROR_MESSAGE = 'An error has occurred, please take a screenshot with the URL and send it to IT.';
  public static final Id STANDARD_PRICEBOOK_ID = '01s6A0000031LaYQAU'; //Test.isRunningTest() ? Test.getStandardPricebookId() : [SELECT Id FROM PriceBook2 WHERE isStandard = true LIMIT 1].Id;
Can somebody try and paste the working code here. It would be a great help.
Thanks in advance.

Hi, I am facing error - Ensure that you implement the Queueable interface in the AnnouncementQueueable class. 

I am getting proper response....and implemented trigger, Product2Helper and AnnouncementQueable class as per the trailhead 

Follwoing is my code -
product2Trigger --->
trigger product2Trigger on Product2 (after update) {

Product2Helper Class ---->
public class Product2Helper {

     * @description List of CollaborationGroup used in both business and test logic
    static List<CollaborationGroup> COLLABORATION_GROUP = [
        SELECT Id
        FROM CollaborationGroup
        OR Name = :('TEST'+Constants.INVENTORY_ANNOUNCEMENTS)
        LIMIT 1

     * @name afterUpdate
     * @description called by product2 Trigger on After Update
     * @param List<Product2> newList
     * @param List<Product2> oldList
    public static void AfterUpdate(List<Product2> prodList, List<Product2> prodOldList){
        //ToDo: Declare a List of Product2 records named needsAnnouncement

        //ToDo: Declare a Map of Strings to Inventory_Setting__mdt records

        //ToDo: Loop through a query of Inventory_Setting__mdt records and populate the Map with Name as the key

        //ToDo: Loop through the Products in newList
        // Use the corresponding Inventory Setting record to determine the correct Low Quantity Alert
        // If the Product's Quantity Remaining has been changed to less than the Low Quantity Alert
        //      add it to the needsAnnouncement list

        //ToDo: Pass records to the postAlerts method
        List<Product2> needsAnnouncement = new List<Product2>();
        Map<String,Inventory_Setting__mdt> mapInventory = new Map<String,Inventory_Setting__mdt>();
        for(Inventory_Setting__mdt inv : [select id, DeveloperName, Low_Quantity_Alert__c from Inventory_Setting__mdt]){
        for(integer i=0;i<prodList.size();i++)
            if(mapInventory.get(prodList[i].Family).Low_Quantity_Alert__c > prodList[i].Quantity_Remaining__c && 
                mapInventory.get(prodList[i].Family).Low_Quantity_Alert__c <= prodOldList[i].Quantity_Remaining__c)

     * @name postAlerts
     * @description called by product2 Trigger on After Update
     * @param List<Product2> productList
    public static void PostAlerts(List<Product2> productList){
        List<ConnectApi.AnnouncementInput> toPost = new List<ConnectApi.AnnouncementInput>();
        for ( Product2 p : productList ){
            // ToDo: Construct a new AnnouncementInput for the Chatter Group so that it:
            // expires in a day
            // does not notify users via email.
            // and has a text body that includes the name of the product followed by the INVENTORY_LEVEL_LOW constant
            ConnectApi.MessageBodyInput msgBody = new ConnectApi.MessageBodyInput();
            ConnectApi.AnnouncementInput tempPost = new ConnectApi.AnnouncementInput();
            ConnectApi.TextSegmentInput textSegment = new ConnectApi.TextSegmentInput();
            textSegment.text = p.Name +'  '+ Constants.INVENTORY_LEVEL_LOW;
            msgBody.messageSegments = new List<ConnectApi.MessageSegmentInput>();
            tempPost.body = msgBody;
            tempPost.expirationDate = DateTime.Now().AddDays(1);
            tempPost.parentId = COLLABORATION_GROUP[0].id;
            tempPost.sendEmails = false;
        // ToDo: Create and enqueue an instance of the announcementQueuable class with the list of Products
        AnnouncementQueueable annQue = new AnnouncementQueueable(toPost);
        //annQue.toPost = toPost;

AnnouncementQueueable Class ---->
 * @name AnnouncementQueueable
 * @description This class posts Chatter Announcements
public class AnnouncementQueueable implements Queueable{

    public List<ConnectApi.AnnouncementInput> toPost;
    public AnnouncementQueueable(List<ConnectApi.AnnouncementInput> annList)
        toPost = annList;

    //ToDo: Modify this class to implement the Queueable interface and call the postAnnouncements method
    public void execute(System.QueueableContext context){

     * @name postAnnouncements
     * @description This method is provided for you to facilitate the Super Badge
    public static void PostAnnouncements(List<ConnectApi.AnnouncementInput> announcements){
        while ( announcements.size() > 0 ){
            if ( Limits.getDMLStatements() < Limits.getLimitDMLStatements() && !test.isRunningTest() ){
                ConnectApi.AnnouncementInput a = announcements.remove(0);
                ConnectApi.Announcements.postAnnouncement('Internal', a);
            } else {
        if ( announcements.size() > 0 && !test.isRunningTest() ){
            AnnouncementQueueable q = new AnnouncementQueueable(announcements);
            //q.toPost = announcements;
            //ToDo: Enqueue the above instance of announcementQueueable


Any help would be really appreciated.
I am attempting to use a DataTable to show individual field values as "cell" values in a spreadsheet on my VisualForce page. I'm doing this by stacking the fields upon one another. This is the actual syntax I am using : 

<apex:pageblocksection title="Risk Assessment" showheader="true" collapsible="true" columns="1">
<apex:dataTable value="{!Vendor_Product_Review__c}" var="pitem" border="4"  align="center" cellpadding="8" > 
                             <apex:column headerValue="">
                             <apex:outputLabel >Credit   </apex:outputLabel><br></br>
                             <apex:outputLabel >Liquidity   </apex:outputLabel><br></br>
                             <apex:outputLabel >Transaction   </apex:outputLabel><br></br>
                             <apex:outputLabel >Foreign Exchange   </apex:outputLabel>
                             <apex:column headerValue="P">
                                 <apex:inputfield value="{!pitem.CreditRiskProbability__c}"/><br></br>
                                 <apex:inputfield value="{!pitem.LiquidityRiskProbability__c}"/><br></br>
                                 <apex:inputfield value="{!pitem.TransactionRiskProbability__c}"/><br></br>
                                 <apex:inputfield value="{!pitem.ForeignExchangeRiskProbability__c}"/><br></br>
                             <apex:column headerValue="S">   
                                <apex:inputfield value="{!pitem.CreditRiskSeverity__c}"/><br></br>
                                <apex:inputfield value="{!pitem.LiquidityRiskSeverity__c}"/><br></br>
                                <apex:inputfield value="{!pitem.TransactionRiskSeverity__c}"/><br></br>
                                <apex:inputfield value="{!pitem.ForeignExchangeRiskSeverity__c}"/><br></br>
                             <apex:column headerValue="C">
                              <apex:inputfield value="{!pitem.CreditControlAdequacy__c}"/><br></br>   
                              <apex:inputfield value="{!pitem.LiquidityControlAdequacy__c}"/><br></br>      
                              <apex:inputfield value="{!pitem.TransactionControlAdequacy__c}"/><br></br>      
                              <apex:inputfield value="{!pitem.ForeignExchangeControlAdequacy__c}"/><br></br>                           

Is there a way for me to have the horizontal grid lines appear between each individual InputField in order to make them appear to be in their own "cell" ?

Thank you for your input.


  • November 13, 2015
  • Like
  • 0