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
Poonam Agarwal 7Poonam Agarwal 7 

Advance Apex Specialist Step 6 - Stuck on that challenge

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.AfterUpdate((List<Product2>)trigger.new,(List<Product2>)trigger.old);
   
}

Product2Helper Class ---->
public class Product2Helper {

    /**
     * @name COLLABORATION_GROUP
     * @description List of CollaborationGroup used in both business and test logic
    **/
    static List<CollaborationGroup> COLLABORATION_GROUP = [
        SELECT Id
        FROM CollaborationGroup
        WHERE Name = :Constants.INVENTORY_ANNOUNCEMENTS 
        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]){
            mapInventory.put(inv.DeveloperName,inv);
        }
        
        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)
            {
                needsAnnouncement.add(prodList[i]);
            }
        }
        PostAlerts(needsAnnouncement);
    }

    /**
     * @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>();
            msgBody.messageSegments.add(textSegment);
            
            tempPost.body = msgBody;
            tempPost.expirationDate = DateTime.Now().AddDays(1);
            tempPost.parentId = COLLABORATION_GROUP[0].id;
            tempPost.sendEmails = false;
            toPost.add(tempPost);
         }
    
        // ToDo: Create and enqueue an instance of the announcementQueuable class with the list of Products
        AnnouncementQueueable annQue = new AnnouncementQueueable(toPost);
        //annQue.toPost = toPost;
        system.enqueueJob(annQue);
    }
}

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){
        
        PostAnnouncements(toPost);
    }

    /**
     * @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 {
                break;
            }
        }
        if ( announcements.size() > 0 && !test.isRunningTest() ){
            AnnouncementQueueable q = new AnnouncementQueueable(announcements);
            //q.toPost = announcements;
            system.enqueueJob(q);
            //ToDo: Enqueue the above instance of announcementQueueable
        }
    }

}

Any help would be really appreciated.
Best Answer chosen by Poonam Agarwal 7
SandhyaSandhya (Salesforce Developers) 
Hi,

I would suggest you refer below link for similar discussion.

https://developer.salesforce.com/forums/?id=9060G0000005O9cQAE
 
Please mark it as solved if my reply was helpful. It will make it available for other as the proper solution.
 
Best Regards
Sandhya
 

All Answers

SandhyaSandhya (Salesforce Developers) 
Hi,

I would suggest you refer below link for similar discussion.

https://developer.salesforce.com/forums/?id=9060G0000005O9cQAE
 
Please mark it as solved if my reply was helpful. It will make it available for other as the proper solution.
 
Best Regards
Sandhya
 
This was selected as the best answer
Jakub Godlewski 11Jakub Godlewski 11
Hello! I am facing the same error. The link doesn't work. There is my code of AnnouncementQueueable class:


public class AnnouncementQueueable implements Queueable{ 
    public List<ConnectApi.AnnouncementInput> toPost {get;set;}
    public void execute(System.QueueableContext context) {
        PostAnnouncements(this.toPost);
    }
    public static void PostAnnouncements(List<ConnectApi.AnnouncementInput> announcements){
        System.debug('Post Announcements: ' + announcements);
        while ( announcements.size() > 0 ){
            if ( Limits.getDMLStatements() < Limits.getLimitDMLStatements() && !test.isRunningTest() ){
                ConnectApi.AnnouncementInput a = announcements.remove(0);
                System.debug('a: ' + a);
                ConnectApi.Announcements.postAnnouncement('Internal', a);
            } else {
                break;
            }
        }
        if ( announcements.size() > 0 && !test.isRunningTest() ){
            AnnouncementQueueable q = new AnnouncementQueueable();
            q.toPost = announcements;
            System.enqueueJob(q);
        }
    }
}

Anyone can help?
Learn Salesforce 36Learn Salesforce 36
Remove System. from below line of your code.

 public void execute(System.QueueableContext context) {
Glen NinanGlen Ninan
Thanks, Learn Salesforce 36. Your solution fixed the issue.
Monu Sharma 9Monu Sharma 9
Hi, I'm facing the error- Challenge Not yet complete... here's what's wrong:
Please ensure that when a Product's Quantity Remaining falls below its low level alert, an announcement is posted to the chatter group.


Product2Helper Class
public class Product2Helper {
    
    /**
* @name COLLABORATION_GROUP
* @description List of CollaborationGroup used in both business and test logic
**/
    static List<CollaborationGroup> COLLABORATION_GROUP = [
        SELECT Id
        FROM CollaborationGroup
        WHERE Name = :Constants.INVENTORY_ANNOUNCEMENTS
        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> newList, List<Product2> oldList){
        //ToDo: Declare a List of Product2 records named needsAnnouncement
        List<Product2> needsAnnouncement = new List<Product2>();
        //ToDo: Declare a Map of Strings to Inventory_Setting__mdt records
        Map<String,Inventory_Setting__mdt> inventorySettings = new Map<String,Inventory_Setting__mdt>();
        //ToDo: Loop through a query of Inventory_Setting__mdt records and populate the Map with Name as the key
        for (Inventory_Setting__mdt inventorySetting : [SELECT Id, Low_Quantity_Alert__c, QualifiedApiName, DeveloperName, MasterLabel FROM Inventory_Setting__mdt]) {
            inventorySettings.put(inventorySetting.DeveloperName, inventorySetting);
        }
        //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
        for(Product2 newProd : newList) {
            if(inventorySettings.get(newProd.family)!= null &&
               newProd.Quantity_Remaining__c < inventorySettings.get(newProd.family).Low_Quantity_Alert__c) {
                   needsAnnouncement.add(newProd);
               }
        }
        
        
        //ToDo: Pass records to the postAlerts method
        PostAlerts(needsAnnouncement);
    }
    
    /**
* @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:
            ConnectApi.AnnouncementInput input = new ConnectApi.AnnouncementInput();
            // expires in a day
            input.expirationDate = Date.today().addDays(1);
            // does not notify users via email.
            input.sendEmails = false;
            ConnectApi.MessageBodyInput bodyInput = new ConnectApi.MessageBodyInput();
            ConnectApi.TextSegmentInput textInput = new ConnectApi.TextSegmentInput();
            // and has a text body that includes the name of the product followed by the INVENTORY_LEVEL_LOW constant
            textInput.text = p.Name + ' , ' + Constants.INVENTORY_LEVEL_LOW;
            bodyInput.messageSegments = new List<ConnectApi.TextSegmentInput>();
            bodyInput.messageSegments.add(textInput);
            input.body = bodyInput;
            input.parentId = COLLABORATION_GROUP[0].Id;
               toPost.add(input);
            
        }
        // ToDo: Create and enqueue an instance of the announcementQueuable class with the list of Products
        AnnouncementQueueable annQueueable = new AnnouncementQueueable();
        annQueueable.toPost = toPost;
        System.enqueueJob(annQueueable);
    }
}


Product2Trigger

/**
* @name product2Trigger
* @description Trigger to notify staff of low levels of inventory
**/
trigger product2Trigger on Product2 (after update) {
    
    Product2Helper.AfterUpdate(Trigger.new, Trigger.old);
}


AnnouncementQueueable Class


/**
 * @name AnnouncementQueueable
 * @description This class posts Chatter Announcements
**/
public class AnnouncementQueueable implements Queueable{

    public List<ConnectApi.AnnouncementInput> toPost;

    //ToDo: Modify this class to implement the Queueable interface and call the postAnnouncements method

    public void execute(QueueableContext context) {
        PostAnnouncements(toPost);
    }
    
    /**
     * @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 {
                break;
            }
        }
        if ( announcements.size() > 0 && !test.isRunningTest() ){
            AnnouncementQueueable q = new AnnouncementQueueable();
            q.toPost = announcements;

            //ToDo: Enqueue the above instance of announcementQueueable
            System.enqueueJob(q);
            
        }
    }

}User-added image

Any one can help me?

 
Vinay Ramu 16Vinay Ramu 16
Hi Monu,

You still facing this issue? I too encountered same issue and checked log file and saw 0 records then I corrected.
If yes, this will be due to queue job not executing or typo with group name. Use same code and call the queueable class(AnnouncementQueueable) from execute in anonymous window. Verify in your Org if the naming of the chatter group and announcements are posted.

Thanks,
Vinay