Advanced Apex Specialist Superbadge
Step 1:
Constants Class:
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 = '01s1N000007brPH';
}
Create Custom Metadata type
Step 2:
Order Trigger:
trigger orderTrigger on Order (after update)
{
OrderHelper.AfterUpdate(Trigger.New, Trigger.Old);
}
{
OrderHelper.AfterUpdate(Trigger.New, Trigger.Old);
}
Order Helper
public without sharing class OrderHelper {
public static void AfterUpdate(List<Order> newList, List<Order> oldList){
Set<Id> orderIds = new Set<Id>();
for ( Integer i=0; i< newList.size(); i++ ){
if ( newList[i].Status == Constants.ACTIVATED_ORDER_STATUS && oldList[i].Status == Constants.DRAFT_ORDER_STATUS ){
orderIds.add(newList[i].Id);
}
}
RollUpOrderItems(orderIds);
}
public static void RollUpOrderItems(Set<Id> activatedOrderIds){
Map<Id, Product2> productMap = new Map<Id, Product2>();
for(OrderItem orderLine : [SELECT Id, Product2Id, Product2.Quantity_Ordered__c, Quantity, Order.ActivatedDate
FROM OrderItem WHERE OrderId IN : activatedOrderIds]){
if(!productMap.containsKey(orderLine.Product2Id))
productMap.put(orderLine.Product2Id, new Product2(Id =orderLine.Product2Id, Quantity_Ordered__c=0));
}
for(AggregateResult ag : [SELECT Sum(Quantity), Product2Id FROM OrderItem WHERE Product2Id IN : productMap.keySet() Group By Product2Id]){
Id product2Id = (Id)ag.get('Product2Id');
Product2 prod = productMap.get(product2Id);
prod.Quantity_Ordered__c = (Decimal)ag.get('expr0');
productMap.put(product2Id , prod);
}
try {
if(productMap.values() != null && productMap.values().size() > 0){
update productMap.values();
}
}catch ( Exception e ){
System.debug('#### Exception Executed : '+e.getStackTraceString());
}
}
}
Step 3:
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:actionRegion >
<apex:pageBlock title="Existing Inventory" >
<apex:chart height="250" width="350" data="{!Inventory}">
<apex:axis type="Numeric" position="left" fields="data" title="MT WON" />
<apex:axis type="Category" position="bottom" fields="name" title="Month"/>
<apex:barSeries orientation="horizontal" axis="left" xField="data" yField="name" />
</apex:chart>
</apex:pageBlock>
<apex:pageBlock title="New Products" >
<apex:pageBlockButtons location="top">
<apex:commandButton action="{!save}" value="Save" />
</apex:pageBlockButtons>
<apex:pageBlockButtons location="bottom">
<apex:commandButton action="{!addRows}" value="Add" reRender="orderItemTable,pageMessages"/>
</apex:pageBlockButtons>
<apex:pageBlockTable value="{!productsToInsert}" var="p" id="orderItemTable" >
<apex:column headerValue="{!$ObjectType.Product2.fields.Name.label}" >
<apex:inputText value="{!p.productRecord.Name}" />
</apex:column>
<apex:column headerValue="{!$ObjectType.Product2.fields.Family.label}" >
<apex:selectList value="{!p.productRecord.Family}" size="1" multiselect="false">
<apex:selectOptions value="{!FamilyOptions}"/>
</apex:selectList>
</apex:column>
<apex:column headerValue="{!$ObjectType.Product2.fields.IsActive.label}" >
<apex:inputField value="{!p.productRecord.isActive}" />
</apex:column>
<apex:column headerValue="{!$ObjectType.pricebookEntry.fields.UnitPrice.label}" >
<apex:inputField value="{!p.pricebookEntryRecord.UnitPrice}" />
</apex:column>
<apex:column headerValue="{!$ObjectType.Product2.fields.Initial_Inventory__c.label}" >
<apex:inputField value="{!p.productRecord.Initial_Inventory__c}" />
</apex:column>
</apex:pageBlockTable>
</apex:pageBlock>
</apex:actionRegion>
</apex:form>
</apex:page>
Product2Extension
public class Product2Extension {
//public List<Product2> productsToInsert {get;set;}
public List<ProductWrapper> productsToInsert {get;set;}
public Product2Extension(ApexPages.StandardController controller){
productsToInsert = new List<ProductWrapper>();
addRows();
}
public void AddRows(){
for ( Integer i=0; i<Constants.DEFAULT_ROWS; i++ ){
productsToInsert.add( new productWrapper() );
}
}
//Get family options
public List<SelectOption> GetFamilyOptions() {
List<SelectOption> options = new List<SelectOption>{
new SelectOption(Constants.SELECT_ONE, Constants.SELECT_ONE)
};
for (Schema.PicklistEntry ple : Constants.PRODUCT_FAMILY) {
options.add(new SelectOption(ple.getValue(), ple.getLabel()));
}
return options;
}
public List<ChartHelper.ChartData> GetInventory(){
return ChartHelper.GetInventory();
}
public PageReference Save(){
Savepoint sp = Database.setSavepoint();
try {
List<Product2> products = new List<Product2>();
List<PricebookEntry> entries = new List<PricebookEntry>();
for (ProductWrapper wrp : productsToInsert){
if(wrp.productRecord != null && wrp.pricebookEntryRecord != null){
if(wrp.productRecord.Name != null && wrp.productRecord.Family != null &&
wrp.productRecord.Family != Constants.SELECT_ONE && wrp.productRecord.Initial_Inventory__c != null &&
wrp.pricebookEntryRecord.UnitPrice != null){
products.add(wrp.productRecord);
PricebookEntry entry=wrp.pricebookEntryRecord;
entry.IsActive=true;
entry.Pricebook2Id=constants.STANDARD_PRICEBOOK_ID;
entries.add(entry);
}
}
}
insert products;
for (integer itr=0; itr<entries.size();itr++){
entries[itr].Product2Id=products[itr].id;
}
insert entries;
//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){
Database.rollback(sp);
apexPages.addMessage(new ApexPages.message(ApexPages.Severity.ERROR,constants.ERROR_MESSAGE));
}
return null;
}
//Product Wrapper Class
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);
}
}
}
ChartHelper class
public without sharing class ChartHelper {
@AuraEnabled
public static List<chartData> GetInventory(){
List<chartData> cht = new List<chartData>();
for (AggregateResult ar : [SELECT Family, SUM(Quantity_Remaining__c) FROM Product2
WHERE Quantity_Remaining__c>0
AND IsActive = true
GROUP BY Family]) {
cht.add(new ChartData(String.ValueOf(ar.get('Family')), Integer.ValueOf(ar.get('expr0'))));
}
return cht;
}
public class ChartData {
public String name {get;set;}
public Decimal val {get;set;}
public ChartData(String name, Decimal val){
this.name = name;
this.val = val;
}
}
}
Step 4:
TestDataFactory
/**
* @name TestDataFactory
* @description Contains methods to construct and/or validate commonly used records
**/
public with sharing class TestDataFactory {
/**
* @name ConstructCollaborationGroup
* @description
**/
public static CollaborationGroup ConstructCollaborationGroup(){
//ToDo: Ensure this method returns a single Chatter CollaborationGroup
// whose Name starts with 'TEST' followed by the INVENTORY_ANNOUNCEMENTS constant
// and configured so anyone can join, see and post updates.
CollaborationGroup ChatterGroup = new CollaborationGroup(
Name = 'TEST'+Constants.INVENTORY_ANNOUNCEMENTS,
CollaborationType = 'Public',
IsArchived = false,
IsAutoArchiveDisabled = true
);
return ChatterGroup;
}
/**
* @name CreateProducts
* @description Constructs a list of Product2 records for unit tests
**/
public static list<Product2> ConstructProducts(Integer cnt){
//ToDo: Ensure this method returns a list, of size cnt, of uniquely named Product2 records
// with all the required fields populated
// and IsActive = true
// an Initial Inventory set to 10
// and iterating through the product family picklist values throughout the list.
list<Product2> products=new list<Product2>();
list<Schema.PicklistEntry> pEntries = Constants.PRODUCT_FAMILY;
Integer pVal = 0;
for(Integer i=0;i<cnt;i++){
Product2 pd2=new Product2();
pd2.Name='Product-'+i;
pd2.IsActive = true;
pd2.Initial_Inventory__c = 10;
if(pVal == 4){
pVal = 0;
}
pd2.Family = pEntries.get(pVal).getValue();
pVal++;
products.add(pd2);
}
return products;
}
/**
* @name CreatePricebookEntries
* @description Constructs a list of PricebookEntry records for unit tests
**/
public static list<PriceBookEntry> ConstructPricebookEntries(List<Product2> prods){
//ToDo: Ensure this method returns a corresponding list of PricebookEntries records
// related to the provided Products
// with all the required fields populated
// and IsActive = true
// and belonging to the standard Pricebook
list<PriceBookEntry> entries=new list<PriceBookEntry>();
for(Product2 pd2: prods){
PriceBookEntry pbe=new PriceBookEntry();
pbe.isActive = true;
pbe.UnitPrice = 100;
pbe.Product2Id = pd2.id;
pbe.PriceBook2Id = Constants.STANDARD_PRICEBOOK_ID;
entries.add(pbe);
}
return entries;
}
/**
* @name CreateAccounts
* @description Constructs a list of Account records for unit tests
**/
public static list<Account> ConstructAccounts(Integer cnt){
//ToDo: Ensure this method returns a list of size cnt of uniquely named Account records
// with all of the required fields populated.
list<Account> accounts=new list<Account>();
for(Integer i=0;i<cnt;i++){
Account ac=new Account();
ac.Name = 'Account'+i;
accounts.add(ac);
}
return accounts;
}
/**
* @name CreateContacts
* @description Constructs a list of Contacxt records for unit tests
**/
public static list<Contact> ConstructContacts(Integer cnt, List<Account> accts){
//ToDo: Ensure this method returns a list, of size cnt, of uniquely named Contact records
// related to the provided Accounts
// with all of the required fields populated.
list<Contact> conts=new list<Contact>();
for(Integer i=0;i<cnt;i++){
contact con=new contact();
con.LastName = 'Contact'+i;
con.AccountId = accts.get(i).id;
conts.add(con);
}
return conts;
}
/**
* @name CreateOrders
* @description Constructs a list of Order records for unit tests
**/
public static list<Order> ConstructOrders(Integer cnt, List<Account> accts){
//ToDo: Ensure this method returns a list of size cnt of uniquely named Order records
// related to the provided Accounts
// with all of the required fields populated.
list<Order> orders=new list<Order>();
for(Integer i=0;i<cnt;i++){
Order ord=new Order();
ord.Name = 'Order'+i;
ord.AccountId = accts.get(i).Id;
ord.EffectiveDate = Date.Today();
ord.Status = Constants.DRAFT_ORDER_STATUS;
ord.Pricebook2Id = Constants.STANDARD_PRICEBOOK_ID;
orders.add(ord);
}
return orders;
}
/**
* @name CreateOrderItems
* @description Constructs a list of OrderItem records for unit tests
**/
public static list<OrderItem> ConstructOrderItems(integer cnt, list<pricebookentry> pbes, list<order> ords){
//ToDo: Ensure this method returns a list of size cnt of OrderItem records
// related to the provided Pricebook Entries
// and related to the provided Orders
// with all of the required fields populated.
// Hint: Use the DEFAULT_ROWS constant for Quantity as it will be used in the next challenge
list<OrderItem> orderItems=new list<OrderItem>();
for(Integer i=0;i<cnt;i++){
OrderItem oItem=new OrderItem();
oItem.OrderId = ords.get(i).Id;
oItem.PriceBookEntryId = pbes.get(i).Id;
oItem.Quantity = Constants.DEFAULT_ROWS;
oItem.UnitPrice = 10;
orderItems.add(oItem);
}
return orderItems;
}
/**
* @name SetupTestData
* @description Inserts accounts, contacts, Products, PricebookEntries, Orders, and OrderItems.
**/
public static void InsertTestData(Integer cnt){
//ToDo: Ensure this method calls each of the construct methods
// and inserts the results for use as test data.
list<Account> accounts = ConstructAccounts(cnt);
Insert accounts;
list<Contact> contacts = ConstructContacts(cnt, accounts);
insert contacts;
list<Product2> prods= ConstructProducts(cnt);
insert prods;
list<PriceBookEntry> entries = ConstructPricebookEntries(prods);
insert entries;
list<Order> orders = ConstructOrders(cnt, accounts);
insert orders;
list<OrderItem> ordItems = ConstructOrderItems(cnt, entries, orders);
insert ordItems;
}
}
Step 5
TestDataFactory
public static void VerifyQuantityOrdered(Product2 originalProduct, Product2 updatedProduct, Integer qtyOrdered){
system.AssertEquals(updatedProduct.Quantity_Ordered__c,originalProduct.Quantity_Ordered__c +qtyOrdered);
}
system.AssertEquals(updatedProduct.Quantity_Ordered__c,originalProduct.Quantity_Ordered__c +qtyOrdered);
}
Product2Test
@isTest (seeAllData=false)
private class Product2Tests {
/**
* @name product2Extension_UnitTest
* @description UnitTest for product2Extension
**/
@isTest
static void Product2Extension_UnitTest(){
PageReference pageRef = Page.Product2New;
Test.setCurrentPage(pageRef);
Product2 prod = new Product2(name='Test',isActive=true);
ApexPages.StandardController stdcontroller = new ApexPages.StandardController(prod);
Product2Extension ext = new Product2Extension(stdcontroller);
System.assertEquals(Constants.DEFAULT_ROWS, ext.productsToInsert.size());
ext.addRows();
System.assertEquals(Constants.DEFAULT_ROWS * 2, ext.productsToInsert.size());
for (Integer i = 0; i < 5; i++) {
Product2Extension.ProductWrapper wrapper = ext.productsToInsert[i];
Product2 testProduct = new Product2();
testProduct.Name = 'Test Product_' + i;
testProduct.IsActive = true;
testProduct.Initial_Inventory__c = 20;
testProduct.Family = Constants.PRODUCT_FAMILY[0].getValue();
wrapper.productRecord = testProduct;
PricebookEntry testEntry = new PricebookEntry();
testEntry.IsActive = true;
testEntry.UnitPrice = 10;
wrapper.pricebookEntryRecord = testEntry;
}
Test.startTest();
ext.save();
Test.stopTest();
ext.GetFamilyOptions();
ext.GetInventory();
List<Product2> createdProducts = [
SELECT Id FROM Product2 where Name LIKE :'Test Product%'];
System.assertEquals(5, createdProducts.size());
}
}
OrderTests
@isTest
public class OrderTests {
@testSetup
public static void SetupTestData(){
TestDataFactory.InsertTestData(5);
}
static testmethod void OrderUpdate_UnitTest (){
test.startTest();
Order rec = [select id, Status from Order limit 1];
Product2 prod = [SELECT Family,Id,Name,Quantity_Ordered__c,Quantity_Remaining__c FROM Product2 limit 1];
system.debug('kkk '+prod.Quantity_Ordered__c);
rec.status = constants.ACTIVATED_ORDER_STATUS;
Update rec;
Product2 updatedprod = [SELECT Family,Id,Name,Quantity_Ordered__c,Quantity_Remaining__c FROM Product2 limit 1];
system.debug('kkk '+updatedprod.Quantity_Ordered__c);
TestDataFactory.VerifyQuantityOrdered(prod,updatedprod,constants.DEFAULT_ROWS);
Test.stopTest();
}
}
Step 6
Create a chatter group name Inventory Announcements with correct discription
AnnouncementQueueable
/**
* @name AnnouncementQueueable
* @description This class posts Chatter Announcements
**/
public class AnnouncementQueueable implements System.Queueable{
public List<ConnectApi.AnnouncementInput> toPost;
public AnnouncementQueueable(List<ConnectApi.AnnouncementInput> toPost){
this.toPost = toPost;
}
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 {
AnnouncementQueueable announcementQueuable
= new AnnouncementQueueable(announcements);
// announcementQueuable.toPost = announcements;
System.enqueueJob(announcementQueuable);
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
}
}
}
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 = 'group name'
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){
// public static void AfterUpdate(List<Product2> newList, List<Product2> oldList){
List<Product2> needsAnnouncement = new List<Product2>();
Map<String, Inventory_Setting__mdt> records =
new Map<String, Inventory_Setting__mdt>();
List<Inventory_Setting__mdt> inventorySettings = [SELECT Label, Low_Quantity_Alert__c FROM Inventory_Setting__mdt];
for(Inventory_Setting__mdt inventorySetting:inventorySettings){
records.put(inventorySetting.Label,inventorySetting);
}
for(Integer i=0;i<newList.size();i++){
Inventory_Setting__mdt inventorySetting =
(Inventory_Setting__mdt)records.get(newList[i].Family);
Integer alertQuantity = (Integer)inventorySetting.Low_Quantity_Alert__c;
if( newList[i].Quantity_Remaining__c <= alertQuantity ){
needsAnnouncement.add(newList[i]);
}
/*(if( newList[i].Quantity_Remaining__c <= alertQuantity &&
oldList[i].Quantity_Remaining__c > alertQuantity){
needsAnnouncement.add(newList[i]);
}*/
}
PostAlerts(needsAnnouncement);
//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
}
/**
* @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 ){
ConnectApi.AnnouncementInput announcement =
new ConnectApi.AnnouncementInput();
ConnectApi.MessageBodyInput messageBodyInput =
new ConnectApi.MessageBodyInput();
List<ConnectApi.MessageSegmentInput> messageSegmentInput =
new List<ConnectApi.MessageSegmentInput>();
ConnectApi.TextSegmentInput bodySegmentInput = new ConnectApi.TextSegmentInput();
bodySegmentInput.text = p.Name + ' ' + Constants.INVENTORY_LEVEL_LOW;
messageSegmentInput.add(bodySegmentInput);
messageBodyInput.messageSegments = messageSegmentInput;
announcement.parentId = COLLABORATION_GROUP.get(0).Id;
announcement.sendEmails = false;
announcement.expirationDate = System.today()+1;
announcement.body = messageBodyInput;
toPost.add(announcement);
// 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
}
AnnouncementQueueable announcementQueuable = new AnnouncementQueueable(toPost);
// announcementQueuable.toPost = toPost;
Id jobId = System.enqueueJob(announcementQueuable);
// ToDo: Create and enqueue an instance of the announcementQueuable class with the list of Products
}
}
Product Trigger
/**
* @name product2Trigger
* @description Trigger to notify staff of low levels of inventory
**/
trigger product2Trigger on Product2 (
after update
) {
Product2Helper.AfterUpdate((List<Product2>)Trigger.new);
}
Step 7:
OrderExtension
/**
* @name OrderExtension
* @description This class is provided for you to facilitate the Super Badge
**/
public class OrderExtension {
public Order orderRecord {get;set;}
public List<OrderItem> orderItemList {get;set;}
public String selectedFamily {get;set;}
public List<chartHelper.chartData> pieData {get;set;}
public Decimal total {get;set;}
public Map<Id,OrderItem> orderItemMap;
ApexPages.StandardSetController standardSetController;
public OrderExtension(ApexPages.StandardController standardController){
orderRecord = (Order)standardController.getRecord();
orderItemMap = new Map<id,OrderItem>();
if ( orderRecord.Id != null ){
orderRecord = queryOrderRecord(orderRecord.Id);
}
resetSsc();
total = 0;
for (OrderItem oi : orderRecord.OrderItems) {
orderItemMap.put(oi.Product2Id, oi);
if (oi.Quantity > 0) {
if (null == pieData) {
pieData = new List<ChartHelper.ChartData>();
}
pieData.add(new chartHelper.ChartData(oi.Product2.Name, oi.Quantity * oi.UnitPrice));
total += oi.UnitPrice * oi.Quantity;
}
}
PopulateOrderItems();
}
void resetSsc() {
String query = 'SELECT Name, Product2.Family, Product2.Name, Product2Id, UnitPrice, Product2.Quantity_Remaining__c'
+ ' FROM PricebookEntry WHERE IsActive = TRUE';
if (selectedFamily != null && selectedFamily != Constants.SELECT_ONE) {
query += ' AND Product2.Family = \'' + selectedFamily + '\'';
}
query += ' ORDER BY Name';
standardSetController = new ApexPages.StandardSetController(Database.getQueryLocator(query));
standardSetController.setPageSize(Constants.DEFAULT_ROWS);
}
//ToDo: Implement your own method to populate orderItemList
// that you will call after pagination and/or family selection
void PopulateOrderItems() {
orderItemList = new List<OrderItem>();
for (SObject obj : standardSetController.getRecords()) {
PricebookEntry pbe = (PricebookEntry)obj;
if (orderItemMap.containsKey(pbe.Product2Id)) {
orderItemList.add(orderItemMap.get(pbe.Product2Id));
} else {
orderItemList.add(new OrderItem(
PricebookEntryId=pbe.Id,
Product2Id=pbe.Product2Id,
UnitPrice=pbe.UnitPrice,
Quantity=0,
Product2=pbe.Product2
));
}
}
}
/**
* @name OnFieldChange
* @description
**/
public void OnFieldChange(){
//ToDo: Implement logic to store the values changed on the page
for (OrderItem oi : orderItemList) {
orderItemMap.put(oi.Product2Id, oi);
}
// and populate pieData
pieData = null;
total = 0;
for (OrderItem oi : orderItemMap.values()) {
if (oi.Quantity > 0) {
if (null == pieData) {
pieData = new List<chartHelper.ChartData>();
}
pieData.add(new chartHelper.ChartData(oi.Product2.Name, oi.Quantity * oi.UnitPrice));
// and populate total
total += oi.UnitPrice * oi.Quantity;
}
}
}
/**
* @name SelectFamily
* @description
**/
public void SelectFamily(){
//ToDo: Implement logic to filter based on the selected product family
resetSsc();
PopulateOrderItems();
}
/**
* @name Save
* @description
**/
public void Save(){
//ToDo: Implement logic to save the Order and populated OrderItems
System.Savepoint sp = Database.setSavepoint();
try {
if (null == orderRecord.Pricebook2Id) {
orderRecord.Pricebook2Id = Constants.STANDARD_PRICEBOOK_ID;
}
upsert orderRecord;
List<OrderItem> orderItemsToUpsert = new List<OrderItem>();
List<OrderItem> orderItemsToDelete = new List<OrderItem>();
for (OrderItem oi : orderItemList) {
if (oi.Quantity > 0) {
if (null == oi.OrderId) {
oi.OrderId = orderRecord.Id;
}
orderItemsToUpsert.add(oi);
} else if (oi.Id != null) {
orderItemsToDelete.add(oi);
}
}
upsert orderItemsToUpsert;
delete orderItemsToDelete;
} catch (Exception e) {
Database.rollback(sp);
apexPages.addMessage(new ApexPages.message(ApexPages.Severity.INFO,Constants.ERROR_MESSAGE));
}
}
/**
* @name First
* @description
**/
public void First(){
standardSetController.first();
PopulateOrderItems();
}
/**
* @name Next
* @description
**/
public void Next(){
standardSetController.next();
PopulateOrderItems();
}
/**
* @name Previous
* @description
**/
public void Previous(){
standardSetController.previous();
PopulateOrderItems();
}
/**
* @name Last
* @description
**/
public void Last(){
standardSetController.last();
PopulateOrderItems();
}
/**
* @name GetHasPrevious
* @description
**/
public Boolean GetHasPrevious(){
return standardSetController.getHasPrevious();
}
/**
* @name GetHasNext
* @description
**/
public Boolean GetHasNext(){
return standardSetController.getHasNext();
}
/**
* @name GetTotalPages
* @description
**/
public Integer GetTotalPages(){
return (Integer)Math.ceil(standardSetController.getResultSize() / (Decimal)Constants.DEFAULT_ROWS);
}
/**
* @name GetPageNumber
* @description
**/
public Integer GetPageNumber(){
return standardSetController.getPageNumber();
}
/**
* @name GetFamilyOptions
* @description
**/
public List<SelectOption> GetFamilyOptions() {
List<SelectOption> options = new List<SelectOption>{
new SelectOption(Constants.SELECT_ONE, Constants.SELECT_ONE)
};
for (Schema.PicklistEntry ple : Constants.PRODUCT_FAMILY) {
options.add(new SelectOption(ple.getValue(), ple.getLabel()));
}
return options;
}
/**
* @name QueryOrderRecord
* @description
**/
public static Order QueryOrderRecord(Id orderId){
return [
SELECT Id, AccountId, EffectiveDate, Name, Status, Pricebook2Id,
(
SELECT Id, OrderId, Quantity, UnitPrice, PricebookEntryId, Product2Id,
Product2.Name, Product2.Family, Product2.Quantity_Remaining__c
FROM OrderItems
)
FROM Order
WHERE Id = :orderId
];
}
}
Step 8:
AnnouncementQueueable Class
/**
* @name AnnouncementQueueable
* @description This class posts Chatter Announcements
**/
public class AnnouncementQueueable implements System.Queueable{
public List<ConnectApi.AnnouncementInput> toPost;
public AnnouncementQueueable(List<ConnectApi.AnnouncementInput> toPost){
this.toPost = toPost;
}
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 {
AnnouncementQueueable announcementQueuable
= new AnnouncementQueueable(announcements);
// announcementQueuable.toPost = announcements;
//System.enqueueJob(announcementQueuable);
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
}
}
}
Product2Extension
Public without sharing class Product2Extension {
public List<productWrapper> productsToInsert {get;set;}
public Product2Extension(ApexPages.StandardController controller){
productsToInsert = new List<productWrapper>();
AddRows();
}
//Get select list options//
public List<SelectOption> GetFamilyOptions() {
List<SelectOption> options = new List<SelectOption>{
new SelectOption(Constants.SELECT_ONE, Constants.SELECT_ONE)
};
for (Schema.PicklistEntry ple : Constants.PRODUCT_FAMILY) {
options.add(new SelectOption(ple.getValue(), ple.getLabel()));
}
return options;
}
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 PageReference Save(){
Savepoint sp = Database.setSavepoint();
try {
List<Product2> products = new List<Product2>();
List<PricebookEntry> entries = new List<PricebookEntry>();
for (ProductWrapper wrp : productsToInsert){
if(null!=wrp.productRecord && null!=wrp.pricebookEntryRecord){
if(null!=wrp.productRecord.Name && null!=wrp.productRecord.Family && constants.SELECT_ONE!=wrp.productRecord.Family
&& null!=wrp.productRecord.Initial_Inventory__c && null!=wrp.pricebookEntryRecord.UnitPrice){
products.add(wrp.productRecord);
PricebookEntry entry=wrp.pricebookEntryRecord;
entry.IsActive=true;
entry.Pricebook2Id=constants.STANDARD_PRICEBOOK_ID;
entries.add(entry);
}
}
}
insert products;
for (integer itr=0; itr<entries.size();itr++){
entries[itr].Product2Id=products[itr].id;
}
insert entries;
//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){
Database.rollback(sp);
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);
}
}
}
Product2Helper
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 = 'group name'
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){
// public static void AfterUpdate(List<Product2> newList, List<Product2> oldList){
List<Product2> needsAnnouncement = new List<Product2>();
Map<String, Inventory_Setting__mdt> records =
new Map<String, Inventory_Setting__mdt>();
List<Inventory_Setting__mdt> inventorySettings = [SELECT Label, Low_Quantity_Alert__c FROM Inventory_Setting__mdt];
for(Inventory_Setting__mdt inventorySetting:inventorySettings){
records.put(inventorySetting.Label,inventorySetting);
}
for(Integer i=0;i<newList.size();i++){
Inventory_Setting__mdt inventorySetting =
(Inventory_Setting__mdt)records.get(newList[i].Family);
Integer alertQuantity = (Integer)inventorySetting.Low_Quantity_Alert__c;
if( newList[i].Quantity_Remaining__c <= alertQuantity ){
needsAnnouncement.add(newList[i]);
}
/*(if( newList[i].Quantity_Remaining__c <= alertQuantity &&
oldList[i].Quantity_Remaining__c > alertQuantity){
needsAnnouncement.add(newList[i]);
}*/
}
PostAlerts(needsAnnouncement);
//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
}
/**
* @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 ){
ConnectApi.AnnouncementInput announcement =
new ConnectApi.AnnouncementInput();
ConnectApi.MessageBodyInput messageBodyInput =
new ConnectApi.MessageBodyInput();
List<ConnectApi.MessageSegmentInput> messageSegmentInput =
new List<ConnectApi.MessageSegmentInput>();
ConnectApi.TextSegmentInput bodySegmentInput = new ConnectApi.TextSegmentInput();
bodySegmentInput.text = p.Name + ' ' + Constants.INVENTORY_LEVEL_LOW;
messageSegmentInput.add(bodySegmentInput);
messageBodyInput.messageSegments = messageSegmentInput;
announcement.parentId = COLLABORATION_GROUP.get(0).Id;
announcement.sendEmails = false;
announcement.expirationDate = System.today()+1;
announcement.body = messageBodyInput;
toPost.add(announcement);
// 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
}
AnnouncementQueueable announcementQueuable = new AnnouncementQueueable(toPost);
// announcementQueuable.toPost = toPost;
Id jobId = System.enqueueJob(announcementQueuable);
// ToDo: Create and enqueue an instance of the announcementQueuable class with the list of Products
}
}
TestDataFactory
/**
* @name TestDataFactory
* @description Contains methods to construct and/or validate commonly used records
**/
public with sharing class TestDataFactory {
public static List<Product2> prods;
public static List<PricebookEntry> entries;
public static List<Order> orders;
public static List<OrderItem> orderItems;
/**
* @name ConstructCollaborationGroup
* @description
**/
public static CollaborationGroup ConstructCollaborationGroup(){
//ToDo: Ensure this method returns a single Chatter CollaborationGroup
// whose Name starts with 'TEST' followed by the INVENTORY_ANNOUNCEMENTS constant
// and configured so anyone can join, see and post updates.
CollaborationGroup ChatterGroup = new CollaborationGroup(
Name = 'TEST'+Constants.INVENTORY_ANNOUNCEMENTS,
CollaborationType = 'Public',
IsArchived = false,
IsAutoArchiveDisabled = true
);
return ChatterGroup;
}
/**
* @name CreateProducts
* @description Constructs a list of Product2 records for unit tests
**/
public static list<Product2> ConstructProducts(Integer cnt){
//ToDo: Ensure this method returns a list, of size cnt, of uniquely named Product2 records
// with all the required fields populated
// and IsActive = true
// an Initial Inventory set to 10
// and iterating through the product family picklist values throughout the list.
list<Product2> products=new list<Product2>();
list<Schema.PicklistEntry> pEntries = Constants.PRODUCT_FAMILY;
Integer pVal = 0;
for(Integer i=0;i<cnt;i++){
Product2 pd2=new Product2();
pd2.Name='Product-'+i;
pd2.IsActive = true;
pd2.Initial_Inventory__c = 10;
if(pVal == 4){
pVal = 0;
}
pd2.Family = pEntries.get(pVal).getValue();
pVal++;
products.add(pd2);
}
return products;
}
/**
* @name CreatePricebookEntries
* @description Constructs a list of PricebookEntry records for unit tests
**/
public static list<PriceBookEntry> ConstructPricebookEntries(List<Product2> prods){
//ToDo: Ensure this method returns a corresponding list of PricebookEntries records
// related to the provided Products
// with all the required fields populated
// and IsActive = true
// and belonging to the standard Pricebook
list<PriceBookEntry> entries=new list<PriceBookEntry>();
for(Product2 pd2: prods){
PriceBookEntry pbe=new PriceBookEntry();
pbe.isActive = true;
pbe.UnitPrice = 100;
pbe.Product2Id = pd2.id;
pbe.PriceBook2Id = Constants.STANDARD_PRICEBOOK_ID;
entries.add(pbe);
}
return entries;
}
/**
* @name CreateAccounts
* @description Constructs a list of Account records for unit tests
**/
public static list<Account> ConstructAccounts(Integer cnt){
//ToDo: Ensure this method returns a list of size cnt of uniquely named Account records
// with all of the required fields populated.
list<Account> accounts=new list<Account>();
for(Integer i=0;i<cnt;i++){
Account ac=new Account();
ac.Name = 'Account'+i;
accounts.add(ac);
}
return accounts;
}
/**
* @name CreateContacts
* @description Constructs a list of Contacxt records for unit tests
**/
public static list<Contact> ConstructContacts(Integer cnt, List<Account> accts){
//ToDo: Ensure this method returns a list, of size cnt, of uniquely named Contact records
// related to the provided Accounts
// with all of the required fields populated.
list<Contact> conts=new list<Contact>();
for(Integer i=0;i<cnt;i++){
contact con=new contact();
con.LastName = 'Contact'+i;
con.AccountId = accts.get(i).id;
conts.add(con);
}
return conts;
}
/**
* @name CreateOrders
* @description Constructs a list of Order records for unit tests
**/
public static list<Order> ConstructOrders(Integer cnt, List<Account> accts){
//ToDo: Ensure this method returns a list of size cnt of uniquely named Order records
// related to the provided Accounts
// with all of the required fields populated.
list<Order> orders=new list<Order>();
for(Integer i=0;i<cnt;i++){
Order ord=new Order();
ord.Name = 'Order'+i;
ord.AccountId = accts.get(i).Id;
ord.EffectiveDate = Date.Today();
ord.Status = Constants.DRAFT_ORDER_STATUS;
ord.Pricebook2Id = Constants.STANDARD_PRICEBOOK_ID;
orders.add(ord);
}
return orders;
}
/**
* @name CreateOrderItems
* @description Constructs a list of OrderItem records for unit tests
**/
public static list<OrderItem> ConstructOrderItems(integer cnt, list<pricebookentry> pbes, list<order> ords){
//ToDo: Ensure this method returns a list of size cnt of OrderItem records
// related to the provided Pricebook Entries
// and related to the provided Orders
// with all of the required fields populated.
// Hint: Use the DEFAULT_ROWS constant for Quantity as it will be used in the next challenge
list<OrderItem> orderItems=new list<OrderItem>();
for(Integer i=0;i<cnt;i++){
OrderItem oItem=new OrderItem();
oItem.OrderId = ords.get(i).Id;
oItem.PriceBookEntryId = pbes.get(i).Id;
oItem.Quantity = Constants.DEFAULT_ROWS;
oItem.UnitPrice = (Decimal)10;
orderItems.add(oItem);
}
return orderItems;
}
public static void VerifyQuantityOrdered(Product2 originalProduct, Product2 updatedProduct, Integer qtyOrdered){
//updatedProduct.Quantity_Ordered__c ==originalProduct.Quantity_Ordered__c +qtyOrdered;
system.AssertEquals(updatedProduct.Quantity_Ordered__c,originalProduct.Quantity_Ordered__c +qtyOrdered);
}
/**
* @name SetupTestData
* @description Inserts accounts, contacts, Products, PricebookEntries, Orders, and OrderItems.
**/
public static void InsertTestData(Integer cnt){
//ToDo: Ensure this method calls each of the construct methods
// and inserts the results for use as test data.
list<Account> accounts = ConstructAccounts(cnt);
Insert accounts;
list<Contact> contacts = ConstructContacts(cnt, accounts);
insert contacts;
prods= ConstructProducts(cnt);
insert prods;
entries = ConstructPricebookEntries(prods);
insert entries;
orders = ConstructOrders(cnt, accounts);
insert orders;
orderItems = ConstructOrderItems(cnt, entries, orders);
insert orderItems;
}
}
OrderHelper
public without sharing class OrderHelper {
public static void AfterUpdate(List<Order> newList, List<Order> oldList){
Set<Id> orderIds = new Set<Id>();
for ( Integer i=0; i< newList.size(); i++ ){
if ( newList[i].Status == Constants.ACTIVATED_ORDER_STATUS && oldList[i].Status == Constants.DRAFT_ORDER_STATUS ){
orderIds.add(newList[i].Id);
}
}
RollUpOrderItems(orderIds);
}
public static void RollUpOrderItems(Set<Id> activatedOrderIds){
Map<Id, Product2> productMap = new Map<Id, Product2>();
for(OrderItem orderLine : [SELECT Id, Product2Id, Product2.Quantity_Ordered__c, Quantity, Order.ActivatedDate
FROM OrderItem WHERE OrderId IN : activatedOrderIds]){
if(!productMap.containsKey(orderLine.Product2Id))
productMap.put(orderLine.Product2Id, new Product2(Id =orderLine.Product2Id, Quantity_Ordered__c=0));
}
for(AggregateResult ag : [SELECT Sum(Quantity), Product2Id FROM OrderItem WHERE Product2Id IN : productMap.keySet() Group By Product2Id]){
Id product2Id = (Id)ag.get('Product2Id');
Product2 prod = productMap.get(product2Id);
prod.Quantity_Ordered__c = (Decimal)ag.get('expr0');
productMap.put(product2Id , prod);
}
try {
if(productMap.values() != null && productMap.values().size() > 0){
update productMap.values();
}
}catch ( Exception e ){
System.debug('#### Exception Executed : '+e.getStackTraceString());
}
}
}
OrderExtension
/**
* @name OrderExtension
* @description This class is provided for you to facilitate the Super Badge
**/
public class OrderExtension {
public Order orderRecord {get;set;}
public List<OrderItem> orderItemList {get;set;}
public String selectedFamily {get;set;}
public List<chartHelper.chartData> pieData {get;set;}
public Decimal total {get;set;}
public Map<Id,OrderItem> orderItemMap;
ApexPages.StandardSetController standardSetController;
public OrderExtension(ApexPages.StandardController standardController){
orderRecord = (Order)standardController.getRecord();
orderItemMap = new Map<id,OrderItem>();
if ( orderRecord.Id != null ){
orderRecord = queryOrderRecord(orderRecord.Id);
}
resetSsc();
total = 0;
for (OrderItem oi : orderRecord.OrderItems) {
orderItemMap.put(oi.Product2Id, oi);
if (oi.Quantity > 0) {
if (null == pieData) {
pieData = new List<ChartHelper.ChartData>();
}
pieData.add(new chartHelper.ChartData(oi.Product2.Name, oi.Quantity * oi.UnitPrice));
total += oi.UnitPrice * oi.Quantity;
}
}
PopulateOrderItems();
}
void resetSsc() {
String query = 'SELECT Name, Product2.Family, Product2.Name, Product2Id, UnitPrice, Product2.Quantity_Remaining__c'
+ ' FROM PricebookEntry WHERE IsActive = TRUE';
if (selectedFamily != null && selectedFamily != Constants.SELECT_ONE) {
query += ' AND Product2.Family = \'' + selectedFamily + '\'';
}
query += ' ORDER BY Name';
standardSetController = new ApexPages.StandardSetController(Database.getQueryLocator(query));
standardSetController.setPageSize(Constants.DEFAULT_ROWS);
}
//ToDo: Implement your own method to populate orderItemList
// that you will call after pagination and/or family selection
void PopulateOrderItems() {
orderItemList = new List<OrderItem>();
for (SObject obj : standardSetController.getRecords()) {
PricebookEntry pbe = (PricebookEntry)obj;
if (orderItemMap.containsKey(pbe.Product2Id)) {
orderItemList.add(orderItemMap.get(pbe.Product2Id));
} else {
orderItemList.add(new OrderItem(
PricebookEntryId=pbe.Id,
Product2Id=pbe.Product2Id,
UnitPrice=pbe.UnitPrice,
Quantity=0,
Product2=pbe.Product2
));
}
}
}
/**
* @name OnFieldChange
* @description
**/
public void OnFieldChange(){
//ToDo: Implement logic to store the values changed on the page
for (OrderItem oi : orderItemList) {
orderItemMap.put(oi.Product2Id, oi);
}
// and populate pieData
pieData = null;
total = 0;
for (OrderItem oi : orderItemMap.values()) {
if (oi.Quantity > 0) {
if (null == pieData) {
pieData = new List<chartHelper.ChartData>();
}
pieData.add(new chartHelper.ChartData(oi.Product2.Name, oi.Quantity * oi.UnitPrice));
// and populate total
total += oi.UnitPrice * oi.Quantity;
}
}
}
/**
* @name SelectFamily
* @description
**/
public void SelectFamily(){
//ToDo: Implement logic to filter based on the selected product family
resetSsc();
PopulateOrderItems();
}
/**
* @name Save
* @description
**/
public void Save(){
//ToDo: Implement logic to save the Order and populated OrderItems
System.Savepoint sp = Database.setSavepoint();
try {
if (null == orderRecord.Pricebook2Id) {
orderRecord.Pricebook2Id = Constants.STANDARD_PRICEBOOK_ID;
}
upsert orderRecord;
List<OrderItem> orderItemsToUpsert = new List<OrderItem>();
List<OrderItem> orderItemsToDelete = new List<OrderItem>();
for (OrderItem oi : orderItemList) {
if (oi.Quantity > 0) {
if (null == oi.OrderId) {
oi.OrderId = orderRecord.Id;
}
orderItemsToUpsert.add(oi);
} else if (oi.Id != null) {
orderItemsToDelete.add(oi);
}
}
upsert orderItemsToUpsert;
delete orderItemsToDelete;
} catch (Exception e) {
Database.rollback(sp);
apexPages.addMessage(new ApexPages.message(ApexPages.Severity.INFO,Constants.ERROR_MESSAGE));
}
}
/**
* @name First
* @description
**/
public void First(){
standardSetController.first();
PopulateOrderItems();
}
/**
* @name Next
* @description
**/
public void Next(){
standardSetController.next();
PopulateOrderItems();
}
/**
* @name Previous
* @description
**/
public void Previous(){
standardSetController.previous();
PopulateOrderItems();
}
/**
* @name Last
* @description
**/
public void Last(){
standardSetController.last();
PopulateOrderItems();
}
/**
* @name GetHasPrevious
* @description
**/
public Boolean GetHasPrevious(){
return standardSetController.getHasPrevious();
}
/**
* @name GetHasNext
* @description
**/
public Boolean GetHasNext(){
return standardSetController.getHasNext();
}
/**
* @name GetTotalPages
* @description
**/
public Integer GetTotalPages(){
return (Integer)Math.ceil(standardSetController.getResultSize() / (Decimal)Constants.DEFAULT_ROWS);
}
/**
* @name GetPageNumber
* @description
**/
public Integer GetPageNumber(){
return standardSetController.getPageNumber();
}
/**
* @name GetFamilyOptions
* @description
**/
public List<SelectOption> GetFamilyOptions() {
List<SelectOption> options = new List<SelectOption>{
new SelectOption(Constants.SELECT_ONE, Constants.SELECT_ONE)
};
for (Schema.PicklistEntry ple : Constants.PRODUCT_FAMILY) {
options.add(new SelectOption(ple.getValue(), ple.getLabel()));
}
return options;
}
/**
* @name QueryOrderRecord
* @description
**/
public static Order QueryOrderRecord(Id orderId){
return [
SELECT Id, AccountId, EffectiveDate, Name, Status, Pricebook2Id,
(
SELECT Id, OrderId, Quantity, UnitPrice, PricebookEntryId, Product2Id,
Product2.Name, Product2.Family, Product2.Quantity_Remaining__c
FROM OrderItems
)
FROM Order
WHERE Id = :orderId
];
}
}
Happy Coding !!!
Test run fails:
ReplyDeleteSystem.AssertException: Assertion Failed: Expected : 0, Actual 5
Stack Track:
class.TestDataFactory.VerifyQuanityOrdered: Line 159, column 1
Class.OrderTests.OrderUpdate_UnitTest: line 16, column 1
Same here..anyone please help
Deletesame issue. Did anyone resolve this issue?
DeleteUse this
Delete@isTest
private class OrderTests {
@testSetup static void SetupTestData(){
TestDataFactory.InsertTestData(1);
}
@isTest private static void OrderUpdate_UnitTest(){
Test.startTest();
Order o = [SELECT Id, Status FROM Order LIMIT 1];
Product2 p = [SELECT Id, Family, Name, Quantity_Ordered__c, Quantity_Remaining__c FROM Product2 LIMIT 1];
o.status = constants.ACTIVATED_ORDER_STATUS;
Update o;
Product2 updatedp = [SELECT Id, Family, Name, Quantity_Ordered__c, Quantity_Remaining__c FROM Product2 LIMIT 1];
TestDataFactory.VerifyQuantityOrdered(p, updatedp, constants.DEFAULT_ROWS);
Test.stopTest();
}
@isTest private static void OrderExtension_UnitTest(){
PageReference pageRef = Page.OrderEdit;
Test.setCurrentPage(pageRef);
Order o = [SELECT Id, Status FROM Order LIMIT 1];
ApexPages.StandardController stdcontroller = new ApexPages.StandardController(o);
OrderExtension ext = new OrderExtension(stdcontroller);
System.assertEquals(1, ext.orderItemList.size());
ext.OnFieldChange();
ext.SelectFamily();
ext.Save();
ext.First();
ext.Next();
ext.Previous();
ext.Last();
ext.GetHasPrevious();
ext.GetHasNext();
ext.GetTotalPages();
ext.GetPageNumber();
List options = ext.GetFamilyOptions();
}
}
still getting the same error
DeleteAlso when submit challange I get this error :
ReplyDeleteChallenge Not yet complete... here's what's wrong:
Ensure that you test all the methods from orderExtension in your orderExtension_unitTest.
I too get same error... what's the fix?
Delete.cognizant?
DeleteIt will work 100%
Delete@isTest
private class OrderTests {
@testSetup static void SetupTestData(){
TestDataFactory.InsertTestData(1);
}
@isTest private static void OrderUpdate_UnitTest(){
Test.startTest();
Order o = [SELECT Id, Status FROM Order LIMIT 1];
Product2 p = [SELECT Id, Family, Name, Quantity_Ordered__c, Quantity_Remaining__c FROM Product2 LIMIT 1];
o.status = constants.ACTIVATED_ORDER_STATUS;
Update o;
Product2 updatedp = [SELECT Id, Family, Name, Quantity_Ordered__c, Quantity_Remaining__c FROM Product2 LIMIT 1];
TestDataFactory.VerifyQuantityOrdered(p, updatedp, constants.DEFAULT_ROWS);
Test.stopTest();
}
@isTest private static void OrderExtension_UnitTest(){
PageReference pageRef = Page.OrderEdit;
Test.setCurrentPage(pageRef);
Order o = [SELECT Id, Status FROM Order LIMIT 1];
ApexPages.StandardController stdcontroller = new ApexPages.StandardController(o);
OrderExtension ext = new OrderExtension(stdcontroller);
System.assertEquals(1, ext.orderItemList.size());
ext.OnFieldChange();
ext.SelectFamily();
ext.Save();
ext.First();
ext.Next();
ext.Previous();
ext.Last();
ext.GetHasPrevious();
ext.GetHasNext();
ext.GetTotalPages();
ext.GetPageNumber();
List options = ext.GetFamilyOptions();
}
}
For step 4 below error occurs -
ReplyDeleteError: Compile Error: Variable does not exist: INVENTORY_ANNOUNCEMENTS at line 12 column 39
you gotta declare that variable manually in the constants class .
Deletepublic 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 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 = '01s5e000003eJb8AAE';
}
Hiii vivek if you had clear can you post your code cause my Announcement class is not covered
DeleteStuck in 7th Challenge. Getting this error
ReplyDeleteChallenge Not yet complete... here's what's wrong:
Ensure that you implement all the pagination methods using the corresponding StandardSetController methods.
For the 7th challenge, there has to be limit.
ReplyDeletequery += ' ORDER BY Name LIMIT 14';
Stuck in 1st challenge. Getting this error.
ReplyDeleteChallenge Not yet complete... here's what's wrong:
Ensure the STANDARD_PRICEBOOK_ID constant returns the expected value
Please help
Do this in query editor:- SELECT Id FROM PriceBook2 WHERE Name = 'Standard Price Book'
DeleteAnd then copy ur STANDARD_PRICEBOOK_ID into your constants class .
'Tier 2 DOD Restricted', 'Tier 1 PA Activation', 'Tier 1 Amber Activation', 'Tier 1 FTTB/N Business Activation', 'Tier 1 FTTB/N High Value Product', 'Tier 1 FTTB/N Enterprise Ethernet', 'Tier 1 FTTB/N Activation', 'Tier 1 FTTC Business Activation', 'Tier 1 FTTC High Value Product', 'Tier 1 FTTC Enterprise Ethernet', 'Tier 1 FTTC Activation', 'Tier 1 FTTP Business Activation', 'Tier 1 FTTP High Value Product', 'Tier 1 FTTP Enterprise Ethernet', 'Tier 1 FTTP Activation', 'Tier 2 HFC Tech Support', 'Tier 1 HFC Business Activation', 'Tier 1 HFC High Value Product', 'Tier 1 HFC Enterprise Ethernet', 'Tier 1 HFC Activation', 'Tier 2 Prescope Tech Support Hotline', 'Tier 1 ACCM', 'Tier 1 Fibre Connect', 'Tier 2 N_to_P', 'Tier 2 C_to_P'
ReplyDeletepublic class Constants {
public static ID getStanderbookId() {
Integer stdPriceBookRecId2;
List stdPriceBookRecId = new List();
stdPriceBookRecId = [SELECT Id, isstandard FROM Pricebook2 WHERE IsStandard=true];
stdPriceBookRecId2 = stdPriceBookRecId[0];
id testid= Test.isRunningTest() ? '01s5j000003ioIcAAI' :stdPriceBookRecId[0].id;
return stdPriceBookRecId2; // '01s5j000003ioIcAAI';
}
public static final Integer DEFAULT_ROWS = 1;
public static final String SELECT_ONE = Label.Select_One;
public static final String INVENTORY_LEVEL_LOW = Label.Inventory_Level_Low;
public static final List 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 = dummycoverage; // '01s5j000003ioIcAAI';// : getStanderbookId() ;
public static final ID dummycoverage = getStanderbookId() ;
}
working fine with above code
Delete//Below code is working fine
ReplyDeletepublic class Constants {
public static ID getStanderbookId() {
Integer stdPriceBookRecId2;
List stdPriceBookRecId = new List();
stdPriceBookRecId = [SELECT Id, isstandard FROM Pricebook2 WHERE IsStandard=true];
stdPriceBookRecId2 = stdPriceBookRecId[0];
id testid= Test.isRunningTest() ? '01s5j000003ioIcAAI' :stdPriceBookRecId[0].id;
return stdPriceBookRecId2; // '01s5j000003ioIcAAI';
}
public static final Integer DEFAULT_ROWS = 1;
public static final String SELECT_ONE = Label.Select_One;
public static final String INVENTORY_LEVEL_LOW = Label.Inventory_Level_Low;
public static final List 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 = dummycoverage; // '01s5j000003ioIcAAI';// : getStanderbookId() ;
public static final ID dummycoverage = getStanderbookId() ;
}
advanced apex specialist superbadge challenge 3
ReplyDeleteI'm stuck on this challenge, returning the following error;
Challenge Not yet complete... here's what's wrong:
Ensure that the save method inserts only Products and Pricebooks for rows that have all fields populated.
Stuck at the same step, any solution???
DeleteStuck on the 7th challenge, recevied this error:
ReplyDeleteEnsure that values are preserved during pagination.
The error is solved?
DeleteI was getting same issues. Resolved by deleting some pricebookEntry record.
DeleteIam also facing same issue but still no solution
ReplyDeleteAnnouncementQueueable is not covered by your test methods
ReplyDeletein Product2Test Unit Test class, here one method was missing so i added Product2Trigger_unitTest Method.
ReplyDelete@isTest
private class Product2Tests {
/**
* @name product2Extension_UnitTest
* @description UnitTest for product2Extension
**/
@isTest
static void Product2Extension_UnitTest(){
PageReference pageRef=Page.Product2New;
Test.setCurrentPage(pageRef);
Product2 prod=new Product2(name='Test',isActive=true);
ApexPages.StandardController stdcontroller=new ApexPages.StandardController(prod);
Product2Extension ext=new Product2Extension(stdcontroller);
System.assertEquals(Constants.DEFAULT_ROWS, ext.productsToInsert.size());
ext.addRows();
System.assertEquals(Constants.DEFAULT_ROWS * 2, ext.productsToInsert.size());
for (Integer i = 0; i < 5; i++) {
Product2Extension.ProductWrapper wrapper = ext.productsToInsert[i];
Product2 testProduct = new Product2();
testProduct.Name = 'Test Product_' + i;
testProduct.IsActive = true;
testProduct.Initial_Inventory__c = 20;
testProduct.Family = Constants.PRODUCT_FAMILY[0].getValue();
wrapper.productRecord = testProduct;
PricebookEntry testEntry = new PricebookEntry();
testEntry.IsActive = true;
testEntry.UnitPrice = 10;
wrapper.pricebookEntryRecord = testEntry;
}
Test.startTest();
ext.save();
Test.stopTest();
ext.GetFamilyOptions();
ext.GetInventory();
List createdProducts = [
SELECT Id FROM Product2 where Name LIKE :'Test Product%'];
System.assertEquals(5, createdProducts.size());
}
@isTest
static void Product2Trigger_UnitTest(){
PageReference pageRef=Page.Product2New;
Test.setCurrentPage(pageRef);
Product2 prod=new Product2(name='Test',isActive=true);
ApexPages.StandardController stdcontroller=new ApexPages.StandardController(prod);
Product2Extension ext=new Product2Extension(stdcontroller);
System.assertEquals(Constants.DEFAULT_ROWS, ext.productsToInsert.size());
ext.addRows();
System.assertEquals(Constants.DEFAULT_ROWS * 2, ext.productsToInsert.size());
for (Integer i = 0; i < 5; i++) {
Product2Extension.ProductWrapper wrapper = ext.productsToInsert[i];
Product2 testProduct = new Product2();
testProduct.Name = 'Test Product_' + i;
testProduct.IsActive = true;
testProduct.Initial_Inventory__c = 20;
testProduct.Family = Constants.PRODUCT_FAMILY[0].getValue();
wrapper.productRecord = testProduct;
PricebookEntry testEntry = new PricebookEntry();
testEntry.IsActive = true;
testEntry.UnitPrice = 10;
wrapper.pricebookEntryRecord = testEntry;
}
Test.startTest();
ext.save();
Test.stopTest();
ext.GetFamilyOptions();
ext.GetInventory();
List createdProducts = [
SELECT Id FROM Product2 where Name LIKE :'Test Product%'];
System.assertEquals(5, createdProducts.size());
}
}
Challenge Not yet complete... here's what's wrong:
ReplyDeleteEnsure that orderTrigger and orderHelper are still working as specified in the earlier challenge.
any idea on this error.getting at final challenge
Deletedont forget to 1.create chatter group and 2. remove none value from family picklist value .3. ProductTests code should be like this @isTest (seeAllData=false)
Deleteprivate class Product2Tests {
/**
* @name product2Extension_UnitTest
* @description UnitTest for product2Extension
**/
@isTest
private static void Product2Extension_UnitTest(){
PageReference pageRef = Page.Product2New;
Test.setCurrentPage(pageRef);
Product2 prod = new Product2(name='Test',isActive=true);
ApexPages.StandardController stdcontroller = new ApexPages.StandardController(prod);
Product2Extension ext = new Product2Extension(stdcontroller);
System.assertEquals(Constants.DEFAULT_ROWS, ext.productsToInsert.size());
ext.addRows();
System.assertEquals(Constants.DEFAULT_ROWS * 2, ext.productsToInsert.size());
for (Integer i = 0; i < 5; i++) {
Product2Extension.ProductWrapper wrapper = ext.productsToInsert[i];
Product2 testProduct = new Product2();
testProduct.Name = 'Test Product_' + i;
testProduct.IsActive = true;
testProduct.Initial_Inventory__c = 20;
testProduct.Family = Constants.PRODUCT_FAMILY[0].getValue();
wrapper.productRecord = testProduct;
PricebookEntry testEntry = new PricebookEntry();
testEntry.IsActive = true;
testEntry.UnitPrice = 10;
wrapper.pricebookEntryRecord = testEntry;
}
Test.startTest();
ext.save();
Test.stopTest();
ext.GetFamilyOptions();
ext.GetInventory();
List createdProducts = [
SELECT Id FROM Product2 where Name LIKE :'Test Product%'];
System.assertEquals(5, createdProducts.size());
}
@isTest private static void Product2Trigger_UnitTest(){
Test.startTest();
Product2 p = new Product2();
p.Name = 'TestProduct';
p.Family = 'Side';
p.IsActive = true;
p.Quantity_Ordered__c = 50;
p.Initial_Inventory__c = 100;
insert p;
CollaborationGroup c = new CollaborationGroup();
c.Name = 'TEST' + Constants.INVENTORY_ANNOUNCEMENTS;
c.Description = 'test';
c.CollaborationType = 'Public';
insert c;
p.Quantity_Ordered__c=96;
update p;
Test.stopTest();
}
}
Ensure that values are preserved during pagination.
ReplyDeleteDid you find the solution of this error?
DeleteStuck on the 8th challenge, received this error:
ReplyDeleteEnsure that values are preserved during pagination.
hey, did you find any solution?
Deleteplease help
SELECT Name, Product2.Family, Product2.Name, Product2Id, UnitPrice, Product2.Quantity_Remaining__c FROM PricebookEntry WHERE IsActive = TRUE ORDER BY Name make sure this query as only 14 rows delete the remaining
DeleteHi , Did you find the solution of this error?
DeleteThis query is returning only 12 rows in my case but still getting this error, can someone please help
DeleteDid you find any Solutions?
DeleteHEELP, I'M STUCKED HERE
DeleteEnsure that you create the orderExtension_unitTest method error comming for the last challenge(Step8)
ReplyDeleteSame error please reply me with me the solution challenge 9.
ReplyDeleteGetting below error for problem - 8 :-
ReplyDeleteComplete the controller extension for a Visualforce page to enable rapid order entry
Hi
ReplyDeleteReceiving an error on step 7. Below is the error please help.
Challenge Not yet complete... here's what's wrong:
Please ensure that the Announcement has a text body that includes the name of the product followed by the INVENTORY_LEVEL_LOW constant.
Challenge Not yet complete... here's what's wrong:
ReplyDeleteEnsure that the Quantity Ordered field on the Product object is correctly calculated when an Order is Activated.
Challenge #3, Error.
CompleteChallenge 3 then change other class
ReplyDeleteHi! I have completed Challenges 1-3 for Advanced Apex Specialist. As per the Business requirements, it is mentioned that "New Millenium Delivery Products" will be created in the salesforce org but, I do not see any of those product records.
ReplyDeleteI have created a new Trailhead playground at least 3 times (and also have used a new Salesforce Developer Edition org). In each case, I have then installed the unmanaged package (package ID: 04tf4000001O5si) as specified in the 'Prework & Notes.' In my attempts for Challenge 4 in the last month, I have not seen these test records created any of the times I have created a new org.
In order to move forward, I created these Product records manually. However, I am getting an error on the Challenge which appears to be incorrect.
Challenge Not yet complete... here's what's wrong:
"Ensure that the getFamilyOptions method returns the correct picklist values."
Here's what the markup is in the Visualforce page for the Product Family Select List values:
<select name="j_id0:form:j_id33:orderItemTable:0:j_id41" size="1"> <option value="Select one">Select one</option>
<option value="Entree">Entree</option>
<option value="Side">Side</option>
<option value="Dessert">Dessert</option>
<option value="Beverage">Beverage</option>
</select>
The challenge error message is, as far as I can tell, inaccurate?
Thank you in advance!
hi
ReplyDeleteHello Guys I am getting below error,
ReplyDeleteChallenge Not yet complete... here's what's wrong:
Ensure that after you clear test data, you run your tests once and the required test coverage is achieved.
cleared all steps and getting error in last step and I run all test classes no failed methods.
Product2Helper has coverage only 83% this class covered from this method OrderUpdate_UnitTest (OrderTests.cls)
ReplyDeletePlease help to increase to 90%
Thanks in advance
because of above issues super badge not completed
DeleteHad to change OrderTests class, OrderUpdate_UnitTest was failing no matter what. Corrected this and I was able to complete the superbadge
ReplyDelete@isTest
public class OrderTests {
@testSetup
public static void SetupTestData(){
TestDataFactory.InsertTestData(5);
}
static testmethod void OrderUpdate_UnitTest (){
insert TestDataFactory.ConstructCollaborationGroup();
Order rec = [select id, Status from Order limit 1];
Product2 prod = [SELECT Family,Id,Name,Quantity_Ordered__c,Quantity_Remaining__c FROM Product2 limit 1];
system.debug('kkk '+prod.Quantity_Ordered__c);
rec.status = constants.ACTIVATED_ORDER_STATUS;
test.startTest();
Update rec;
Test.stopTest();
Product2 updatedprod = [SELECT Family,Id,Name,Quantity_Ordered__c,Quantity_Remaining__c FROM Product2 limit 1];
system.debug('kkk '+updatedprod.Quantity_Ordered__c);
TestDataFactory.VerifyQuantityOrdered(prod,updatedprod,constants.DEFAULT_ROWS);
}
@isTest private static void OrderExtension_UnitTest(){
PageReference pageRef = Page.OrderEdit;
Test.setCurrentPage(pageRef);
Order o = [SELECT Id, Status FROM Order LIMIT 1];
ApexPages.StandardController stdcontroller = new ApexPages.StandardController(o);
OrderExtension ext = new OrderExtension(stdcontroller);
System.assertEquals(5, ext.orderItemList.size());
ext.OnFieldChange();
ext.SelectFamily();
ext.Save();
ext.First();
ext.Next();
ext.Previous();
ext.Last();
ext.GetHasPrevious();
ext.GetHasNext();
ext.GetTotalPages();
ext.GetPageNumber();
List options = ext.GetFamilyOptions();
}
}
Anyone still having issues with this, I found that there are two pricebooks in the org, and that all the items in one pricebook needed to be set to isActive=false; also if there were more than 14 items , then some of the items in the active Pricebook needed to be de-activated, as well, that there would always be 3 pages for the total number of pages query. This superbadge is a pain!
ReplyDeleteEnsure that values are preserved during pagination. I keep getting this error.
DeleteThese are the assertions:
// Initialize the OrderExtension class with a standard controller for a new Order
OrderExtension ext = new OrderExtension(new ApexPages.StandardController(new Order()));
// Perform actions
ext.OnFieldChange();
ext.next();
// Validate assertions
system.assert(ext.orderItemList.size() > 0);
// Assertions after the next() method
system.assert(ext.orderItemList[0].Quantity != 1);
system.assert(ext.orderItemList[0].UnitPrice != 1);
system.assert(ext.orderItemList[1].Quantity != 2);
system.assert(ext.orderItemList[1].UnitPrice != 2);
ext.previous();
// Assertions after the previous() method
system.assert(ext.orderItemList[0].Quantity == 1);
system.assert(ext.orderItemList[0].UnitPrice == 1);
system.assert(ext.orderItemList[1].Quantity == 2);
system.assert(ext.orderItemList[1].UnitPrice == 2);
Full list:
DeleteExecute Anonymous: system.assert(ext.orderItemList.size() > 0);
Execute Anonymous: ext.orderItemList[0].Quantity = 1;
Execute Anonymous: ext.orderItemList[0].UnitPrice = 1;
Execute Anonymous: ext.orderItemList[1].Quantity = 2;
Execute Anonymous: ext.orderItemList[1].UnitPrice = 2;
Execute Anonymous: ext.OnFieldChange();
Execute Anonymous: ext.next();
Execute Anonymous: system.assert(ext.orderItemList[0].Quantity != 1);
Execute Anonymous: system.assert(ext.orderItemList[0].UnitPrice != 1);
Execute Anonymous: system.assert(ext.orderItemList[1].Quantity != 2);
Execute Anonymous: system.assert(ext.orderItemList[1].UnitPrice != 2);
Execute Anonymous: ext.previous();
Execute Anonymous: system.assert(ext.orderItemList[0].Quantity == 1);
Execute Anonymous: system.assert(ext.orderItemList[0].UnitPrice == 1);
Execute Anonymous: system.assert(ext.orderItemList[1].Quantity == 2);
Execute Anonymous: system.assert(ext.orderItemList[1].UnitPrice == 2);
I am stuck at challenge of Unit tests
ReplyDeleteGetting the following error as OrderTests(OrderUpdate_UnitTest) is failing:
System.AssertException: Assertion Failed: Expected: 0, Actual: 5
Class.TestDataFactory.VerifyQuantityOrdered: line 158, column 1
Class.OrderTests.OrderUpdate_UnitTest: line 17, column 1
I'm getting this error at challenge 8 Challenge Not yet complete... here's what's wrong:
ReplyDeleteEnsure that the pie chart is updated when an Order Item value is modified.
.
.
even every thing works fine still getting this error
I am getting this error at challenge 8.
ReplyDeleteChallenge Not yet complete... here's what's wrong:
Ensure that values are preserved during pagination.
I tried by adding a limit 15 to get PricebookEntry in OrderExtension class still not working.
How to resolve this issue?
This comment has been removed by the author.
DeleteHello I am getting error in Challege 9.
ReplyDeleteChallenge Not yet complete... here's what's wrong:
Ensure that you've deleted packageTests.
How can we resolve this?
you have delete the Class named 'packageTests' from your org.
DeleteThis comment has been removed by the author.
ReplyDeleteChallenge Not yet complete... here's what's wrong:
ReplyDeleteEnsure that you have at least 12 products created and that OrderExtension is still working as specified in the earlier challenge.
How to resolve this issues?