(11/05/2019 -11/10/19)
Very Exhaustive & tiring challenge. A lot of concepts to digest, definitely could not make it without trailhead and other google resources.
It took me 5 days with an average of 1+ Hour daily.
I still feel I am far away from where I would want to be with coding. I would love to be in a state where I don’t refer to trailhead or google resources. I guess practice is the only way forward…
1. Automate Record Creation (11/05/2019 5 am to 6 am)
What I Learned
https://trailhead.salesforce.com/en/content/learn/modules/apex_triggers
trigger MaintenanceRequest on Case (before update, after update) {
// call MaintenanceRequestHelper.updateWorkOrders
if (trigger.isAfter){
//if(trigger.isUpdate){
MaintenanceRequestHelper.updateWorkOrders(Trigger.new,Trigger.OldMap);
}
}
public class MaintenanceRequestHelper {
public static void updateWorkOrders(List<Case> ClosedCases,map<id,Case> ClosedCasesOldMap){
// update workorders
for (Case c : ClosedCases){
Case oldCase = ClosedCasesOldMap.get(c.id);
//https://www.sfdc99.com/2014/02/25/comparing-old-and-new-values-in-a-trigger/
Boolean oldCaseisClosed = oldCase.Status.equals('Closed');
System.debug('OldCaseisClosed=' + oldCaseisClosed);
Boolean NewCaseisClosed = C.Status.equals('Closed');
System.debug('NewCaseisClosed=' + NewCaseisClosed);
List<AggregateResult> minMaintananceCyleDays = [SELECT MIN(Maintanance_cycle__c) from Work_Part__C where Maintenance_Request__c =: oldCase.id];
Integer intminMaintananceCyleDays = Integer.valueOf(minMaintananceCyleDays[0].get('expr0'));
System.debug('intminMaintananceCyleDays = ' + intminMaintananceCyleDays);
List <Case> CasetobeCreated = new List<case>();
if(!oldCaseisClosed && NewCaseisClosed ){
if(c.IsClosed == true && (c.Type == 'Repair' || c.Type == 'Routine Maintenance')){
case nc = new case();
nc.Type = 'Routine Maintenance';
nc.Vehicle__c = c.Vehicle__c;
if (c.Equipment__c != null){
nc.Equipment__c = c.Equipment__c;
}
if (c.Subject != null){
nc.Subject = c.Subject;
}
nc.Date_Reported__c = system.today();
if (intminMaintananceCyleDays != null){
nc.Date_Due__c = system.today()+ intminMaintananceCyleDays;
} else
nc.Date_Due__c = system.today();
System.debug('NC: '+ nc);
nc.Status = 'New';
CasetobeCreated.add(nc);
}
insert CasetobeCreated;
}
}
}
}
2. Synchronize Salesforce data with an external system
What I Learned
public with sharing class WarehouseCalloutService {
private static final String WAREHOUSE_URL = 'https://th-superbadge-apex.herokuapp.com/equipment';
// complete this method to make the callout (using @future) to the
// REST endpoint and update equipment on hand.
@Future(callout=true)
public static void runWarehouseEquipmentSync(){
List<Product2> syncprod = new List<Product2>();
Http http = new Http();
HttpRequest request = new HttpRequest();
request.setEndpoint(WAREHOUSE_URL);
request.setMethod('GET');
HttpResponse response = http.send(request);
if(response.getStatusCode()==200){
List<equipmentjson> results = (List<equipmentjson>) JSON.deserialize(response.getBody(), List<equipmentjson>.class);
for(equipmentjson inst : results){
product2 equip = new product2();
equip.Cost__c = inst.cost;
equip.Lifespan_Months__c = inst.lifespan;
equip.Maintenance_Cycle__c = inst.maintenanceperiod;
equip.Name = inst.name;
equip.Current_Inventory__c = inst.quantity;
equip.Replacement_Part__c = inst.replacement;
equip.Warehouse_SKU__c = inst.sku;
syncprod.add(equip);
}
}
if(syncprod != null && syncprod.size() > 0){
UPSERT syncprod Warehouse_SKU__c;
}
}
private class equipmentjson{
private String id;
private Integer cost;
private Integer lifespan;
private Integer maintenanceperiod;
private String name;
private Integer quantity;
private boolean replacement;
private String sku;
}
}
global class WarehouseSyncSchedule implements schedulable {
// implement scheduled code here
global void execute(SchedulableContext sc){
WarehouseCalloutService.runWarehouseEquipmentSync();
}
}
4. Test automation logic
https://trailhead.salesforce.com/en/content/learn/modules/apex_testing
@isTest
public class TestDataFactory {
public static List<case> createmr(String mrtype, integer noofcases){
List<case> mr = new List <case>();
if(noofcases > 0 && mrtype.length() >0 ){
//create vehicle
Vehicle__c v = new Vehicle__c(Name='Jetta 01', VIN_Number__c='Jetta123');
insert v;
//create Equipments: two with different maintenance cycles
List<Product2> equip = new List<Product2>();
equip.add(new Product2(Name='Testequip 01', Warehouse_SKU__c='001', Lifespan_Months__c=12, Maintenance_Cycle__c=60, Replacement_Part__c=true));
equip.add(new Product2(Name='Testequip 02', Warehouse_SKU__c='002', Lifespan_Months__c=24, Maintenance_Cycle__c=30, Replacement_Part__c=true));
insert equip;
//create maintenance requests
for (integer i = 0; i<noofcases; i++) {
mr.add(new Case(Type = mrtype, Status='New', Origin='Phone', Equipment__c=equip[0].id, Subject='Test Case'));
}
insert mr;
//Add two work parts for each maintenance request created
List<Work_Part__c> wp = new List<Work_Part__c>();
for (Case c: mr) {
wp.add(new Work_Part__c(Equipment__c=equip[0].id, Maintenance_Request__c=c.id, Quantity__c=1));
wp.add(new Work_Part__c(Equipment__c=equip[1].id, Maintenance_Request__c=c.id, Quantity__c=2));
}
insert wp;
}
return mr;
}
}
@IsTest
public class MaintenanceRequestTest {
@IsTest
static void TestPositiveSingle() {
//Test Repair maintance request
testmrcreation('Repair', 1, 'Closed');
//Test Routine Maintenance request
testmrcreation('Routine Maintenance', 1, 'Closed');
}
@IsTest
static void TestNegativeSingle() {
//Test Other maintance request
testmrcreation('Other', 1, 'Closed');
//Test Repair maintance request
testmrcreation('Repair', 1, 'Working');
//Test Routin Maintenance request
testmrcreation('Routine Maintenance', 1, 'Escalated');
}
@IsTest
static void TestPositiveBulk() {
//Test Repair maintance request
testmrcreation('Routine Maintenance', 300, 'Closed');
//Test Repair maintance request
testmrcreation('Repair', 300, 'Closed');
}
@IsTest
static void TestNegativeBulk() {
//Test Other maintance request
testmrcreation('Other', 300, 'Closed');
//Test Repair maintance request
testmrcreation('Repair', 300, 'Working');
//Test Routin Maintenance request
testmrcreation('Routine Maintenance', 300, 'Escalated');
}
private static void testmrcreation(string requestType, integer totalCount, string newStatus) {
//Test Repair maintance request
List<Case> testRequests = TestDataFactory.createmr(requestType, totalCount);
for (Case tr: testRequests) {
tr.Status = newStatus;
}
update testRequests;
List<Case> newmrs = [SELECT id, Type, Status, Vehicle__c, Equipment__c, Subject, Origin, Date_Reported__c, Date_Due__c
FROM Case WHERE ParentId IN: testRequests
AND Status = 'New'];
integer expectedTotal = 0;
if(newStatus == 'Closed' && (requestType =='Repair' || requestType == 'Routine Maintenance')) {
expectedTotal = totalCount;
}
// System.assertEquals(expectedTotal,newFollowupRequests.size());
if (expectedTotal>0) {
for (Case newmr: newmrs) {
System.assertEquals('Routine Maintenance', newmr.Type);
System.assertEquals('System Generated Routine Maintenance', newmr.Subject);
System.assertEquals('System', newmr.Origin);
System.assertEquals(Date.today(), newmr.Date_Reported__c);
System.assertEquals(Date.today().addDays(30), newmr.Date_Due__c);
}
}
}
}
5. Test Call out Logic
global class WarehouseCalloutServiceMock implements HttpCalloutMock {
// implement http mock callout
//Mock responce created to test the call out
global HttpResponse respond(HttpRequest request){
System.assertEquals('https://th-superbadge-apex.herokuapp.com/equipment', request.getEndpoint());
System.assertEquals('GET', request.getMethod());
HttpResponse response = new HttpResponse();
response.setHeader('Content-Type', 'application/json');
response.setBody('[{"_id":"55d66226726b611100aaf741","replacement":false,"quantity":5,"name":"Generator 1000 kW","maintenanceperiod":365,"lifespan":120,"cost":5000,"sku":"100003"}]');
response.setStatusCode(200);
return response;
}
}
@isTest
private class WarehouseCalloutServiceTest {
// implement your mock callout test here
@isTest
static void WarehouseEquipmentSync(){
Test.startTest();
// Set mock callout class
Test.setMock(HttpCalloutMock.class, new WarehouseCalloutServiceMock());
// This causes a fake response to be sent from the class that implements HttpCalloutMock.
WarehouseCalloutService.runWarehouseEquipmentSync();
Test.stopTest();
}
}
6.Test scheduling logic
https://trailhead.salesforce.com/content/learn/modules/asynchronous_apex/async_apex_scheduled
@isTest
public class WarehouseSyncSheduleTest {
@isTest static void WarehousescheduleTest(){
String scheduleTime = '00 00 01 * * ?';
Test.startTest();
Test.setMock(HttpCalloutMock.class, new WarehouseCalloutServiceMock());
String jobID=System.schedule('Warehouse Time To Schedule to Test', scheduleTime, new WarehouseSyncSchedule());
Test.stopTest();
//Contains schedule information for a scheduled job. CronTrigger is similar to a cron job on UNIX systems.
// This object is available in API version 17.0 and later.
CronTrigger a=[SELECT Id FROM CronTrigger where NextFireTime > today];
System.assertEquals(jobID, a.Id,'Schedule ');
}
}
So far the best. Its time to build so cool & powerful apps
Exhaustive but a good exercise as the world is moving towards security including crms..
As Salesforce suggest, the best way is to write it down on paper…and start ticking of one by one..
Started this super badge on Aug 4th and completed on Oct 4th….was completely on and off but at the end it just took 3 hours of focussed time.
Highly recommend this SuperBadge, this is more than Real Time Scenario!!
Below is one final dashBoard!
Being Salesforce Admin is just not being able to create Reports and dashboards – It is about how your organization handles Data.
Data comes into Salesforce from Various forms – List Imports, Backend API Integrations and Manual Entry.
This Super badge introduces about how data is imported into salesforce via Data Loader…
I am glad and super excited to have accomplished this dashboard.