ANJARGHOLI.COM

Yolande Anjargholi Online

Most Salesforce admins, and business consultants, are likely familiar with the trials and tribulations of test data, when it comes time for UAT (User Acceptance Testing). I was recently faced with a situation where I needed a few accounts that have at least 150 future events, associated to each account. We needed this test data, so that trainers can go to each of those accounts, re-assign the account, and test a trigger that should fire, and reassign all those future events, to the new account owner’s sales assistant, as indicated on the new owner's user record.

When you are faced with situations like that, you always have the option to use our trusted friend, Data Loader, and insert the 300+ or so records you will need to have ready for your testers, to use during UAT. However, think of what that takes… An excel spreadsheet, columns, mappings, etc. It may be well worth it to dip your toes into writing apex code, and learn how to quickly create such test data, for users, in less than 5 minutes.

In order to quickly execute apex code, you will need to use the Execute Anonymous window in the developer console

- click on the drop down arrow next to your name in top right hand corner
- select Developer Console
- Click on Debug, and select ‘Open Execute Anonymous Window

Execute Anonoymous

Now, you are ready to give it a try. I included a few code examples that I have found useful, and it is easy to modify to fit your own needs.

Create Related Records

For this example, I will add Event Records to a specific Account, for a specific user

 List< Event> EventList = new List<Event>();  
 DateTime myStart = DateTime.newInstance(2016, 05, 27, 10, 01, 01);  
     for(Integer i=0;i<40;i++){        
       Event newEvent = new Event();  
       newEvent.OwnerId = '[record id here]';  
       newEvent.WhatId = '[what id here]';  
       newEvent.StartDateTime = myStart.addMinutes(i + 5);  
       newEvent.EndDateTime = myStart.addMinutes(i + 5 + 30);  
       newEvent.Subject = '1569 test event - ' +i;  
       newEvent.Type = 'Other';  
       newEvent.RecordTypeId = '[recordtype id here if you have multiple record types]';  
       EventList.add(newEvent);  
     }     
 system.debug('Events to be added is - ' +EventList.size());  
 insert EventList;  

Having this basic code example on how to add multiple related records, as a starting point, you can work on adjusting it to create related records for almost any object in your organization. (As a side note, when you specify the max value for i,keep governor limits in mind).

Delete Records

 List< MyObject__c> objectList = [SELECT Id, CreatedDate FROM MyObject__c WHERE CreatedDate = Today LIMIT 5000];
 system.debug('list is - ' + objectList.size());
 delete sosList;  

This is the basic code to quickly delete records you may no longer need after UAT testing, or may simply want to have handy to do some ad-hoc clean-up of your sandbox environments, if needed. You can use workbench to figure out the correct SELECT query that will meet your needs.

Update Records

List< MyObject__c > objectList = new List([SELECT CreatedDate, object_Start_Date__c FROM MyObject__c WHERE object_Start_Date__c = null]);
system.debug('update list is - ' + objectList.size());
List< MyObject__c > objectUpdateList = new List< MyObject__c >;
for (MyObject__c ObjectRecord : objectList){
    MyObject__c .object_Start_Date__c = MyObject__c.CreatedDate;
    objectUpdateList.add(ObjectRecord);
}
system.debug('update list is - ' + objectUpdateList.size());
update objectUpdateList; 

You can copy and paste these code snippest into the Execute Anonymous Window, make the changes as you require, and click on execute. Please note that these are simple, quick code examples - so use it accordingly. If you are dealing with large amounts of data, you may need to make some changes, or consider an alternative approach. Hopefully this helps to get you thinking about situations where you can use Apex code, instead of always only using DataLoader, as the default tool of choice.

Salesforce.com is the Lamborghini of CRM systems, and one of the most essential features of any top rated CRM system, is the sending and tracking of emails. If you are someone that works with salesforce.com in any technical capacity, it is important to have some ‘under the hood’ insight around the capabilities, limitations, and monitoring abilities you have at your disposable, when working with outbound email.  While using workflow or some of the OOB email features may be more familiar to all of us ‘none hard-core’ developers, and admins, it can be very helpful to be more equip with some additional insight, about how APEX can be used to send emails from Salesforce.  If you haven’t already, you are bound to encounter situations where coding is needed to send out an e-mail.

BASIC OVERVIEW

Depending on your requirements, you can send emails programmatically to one, or multiple people. E-mail recipients can either exist as a record in your Salesforce organization (ex. Lead, Contact, Person Account, or User records), or can be any other internal or external individual. You also have the ability to send a single email, or to send mass emails.  Salesforce essentially offers two classes that you can leverage, depending on what you are trying to accomplish: 

>>Send Individual Emails

** APEX Class: SingleEmailMessage 
** Format Options: HTML, Plain text, Salesforce email templates, generated by Visualforce
** Other Considerations: You can send 100 emails per SingleEmailMessage 

>> Send Mass Emails

** APEX Class: MassEmailMessage
** Format Options: HTML, Plain text, Salesforce email templates, (Visualforce email templates are NOT available)
** Other Considerations: You can send mass emails only to contacts, person accounts, leads, and your org’s internal users

GOVERNOR LIMITS

>> VIA UI
Single emails sent using the Salesforce application don't count toward this limit. There’s no limit on sending individual emails to contacts, leads, person accounts, and users in your org directly   from account, contact, lead, opportunity, case, campaign, or custom object pages.

>> VIA API/APEX

This is where it gets a little tricky.

- You can send single emails to a maximum of 1,000 external email addresses per day based on Greenwich Mean Time (GMT)
- Depending on what edition you are using, you can send mass email to a maximum of 1,000 external email addresses per day per org based on Greenwich Mean Time (GMT)

(See the useful links section at the bottom of this post, for easy navigation to the Salesforce quick reference documentation on e-mail limits)

With that said -  if you have code that send an email to a Salesforce user, lead, or contact (i.e. internal to your salesforce organization), you / your dev team should always try to opt for using the ‘TargetObjectID’, instead of using / referencing an actual e-mail address (ex. UserInfo.getUserEmail’). By following this approach, ‘internal’ emails are NOT counted against the limit. Ideally, we want to have all 1000 emails available for when / if we need to send emails to external parties or ‘non-salesforce records’ via the API/APEX. I will include a sample code towards the end of this post, that you copy and use, to try this out, for yourself.

Note - single and mass email limits don't take unique addresses into account, so when you include, for example, This email address is being protected from spambots. You need JavaScript enabled to view it. in your email, 10 times, that will actually count as 10, towards the limit. 

CHECKING YOUR LIMITS / LOGS

When it comes time to take a closer look at the e-mails that have been sent from your Salesforce org, I have found these 3 options, useful:

  1. Check on your remaining / available emails, based on limits for SingleEmailMessage and MassEmailMessage: click here for knowledge article with step by step instructions.
    Checking Email Limit Utalization
  2. Email logs are CSV files that can be requested, and contain information about all email sent through your Salesforce organization over the last 30 days. You can request these logs from setup, without help from Salesforce, and it generates fairly quickly. Click here for step by step instructions on how to get your email logs.

  3. Salesforce can track the status of email in HTML format, including the date the email was sent, first opened and last opened, and the total number of times it was opened. You will need to have e-mail tracking enabled for your organization (Setup | Customize | Activities | Activity Settings | Email Tracking checkbox)  

    When you do, you will have access to the following reporting / tracking features:

    a)Report Type: HTML Email Status Report (for contacts)
    b)Report Fields: when creating normal account and contacts reports, you should have these 2 fields available, to add to your reports: ‘Email      Bounced Reason’, and ‘Email Bounced Date’. 
    c) Related List: You can add the ‘HTML Email Status’ related list to your Lead, Contact, or Person Account page layouts.

           HTML Email Status Related List

TRY IT OUT - SAMPLE CODE

Read more ...

Most of us make use of all the development goodies that Salesforce has to offer, to extend and mold functionality to meet very specific business requirements. As we embark on our journey of designing, developing, implementing, and maintaining custom capabilities, we inevitably face the dull, and daunting task of squinting through the debug log to identify an issue.  Although it may be a headache inducing experience, inspecting the debug log that Salesforce provides is normally very useful for issue resolution, so why should we invest additional effort in creating a more persistent framework for debugging?

  • Consider the situation where a user encounters an issue with a custom feature that you introduced -  As a developer, you know that troubleshooting will require that you re-create the scenario before you can attempt any kind of debugging.

  • Each debug log must be 2MB or smaller. Debug logs that are larger than 2 MB are reduced in size by removing older log lines. However, log lines can be removed from any location, not just the start of the debug log. (What do you do when you have a batch with high record counts and you can't see what the error was, because the log was truncated... figure out if a different log level can work, or try to implement some limits?)

  • How do we make it easy for an administrator to pull a report to review and share any potential issues?

An alternative approach that help address some of these points, is to store exceptions in a custom object. We can accomplish this fairly quickly, and you don't need to be Jedi Apex developer to get it done. (One call out: please review the extended exception class, and modify / enhance it to fit your own requirements to raise custom exceptions. This is just a basic start, that can be improved on) 

1. Create an object to store your exceptions in:

CustomExceptionObject

 2. From the developer console, create a class that extends the built-in Exception Class

(you can get more details here: https://developer.salesforce.com/docs/atlas.en-us.apex_workbook.meta/apex_workbook/apex7_5.htm)

 public class HandleException extends Exception {
    public static void LogException(Exception e){
        try{
            String QueryLimit = '1. SOQL Queries used / SOQL Queries allowed: ' + Limits.getQueries() + '/' + Limits.getLimitQueries();
			String DMLimit = '2. Number of records queried so far /  Number allowed: ' + Limits.getDmlRows() + '/' + Limits.getLimitDmlRows();
			String DMLStat = '3. Number of DML statements used so far / Number allowed: ' +  Limits.getDmlStatements() + '/' + Limits.getLimitDmlStatements();   
			String CPUT = '4. Amount of CPU time (in ms) used so far / CPU usage time (in ms) allowed: ' + Limits.getCpuTime() + '/' + Limits.getLimitCpuTime();
            
            //Create exception record
            CustomException__c exc = new CustomException__c();
            exc.Gov_Limit_Tracking_in_Executed_Code_Cont__c = String.format('{0}\n{1}\n{2}\n{3}',new List{QueryLimit, DMLimit,DMLStat,CPUT});
            exc.Error_Message__c = e.getMessage();
            exc.Exception_Type__c = e.getTypeName();
            exc.Line_Number__c = e.getLineNumber();
            exc.Stack_Trace__c = e.getStackTraceString();
            database.insert(exc);            
        } catch (Exception exc) {
        }            
    } //end method
} //end class
3. You are done. Now all you need is to put your work to the test - create a class that will raise an exception

(In my example below, I am inserting a Merchandise record without a value for required fields)

 public class YolandeTestClass {
    public static void MainProcessing() {
        try {
            Merchandise__c m = new Merchandise__c();
            insert m;
        } catch (Exception e) {
            HandleException.LogException(e);
        }    
    } //end method
} //end class

Run the code in your Execute Anonymous window. YolandeTestClass.mainProcessing();

4. Take a look at the CustomException record that was created

CustomExceptionRecord

I am a big fan of Trailhead, and recently started working through the Lightning Components Basics trail. As part of this module, the Input Data Using Forms is one of the longest and most time consuming units. It definitly takes longer than the estimated time investement of 35 min. I think the only way to work through this module in 35 min is by copy and paste and glancing over the concepts - obviously not a recommended approach. 

This is a very valuable unit that will provide great insight into the Lightning Design System, and gives you hands on experience on how to work with it. I wanted to share the code you will need to pass the challenge for the unit, since I found it a little confusing. I created and leveraged a helper method in the CamplingListcontroller, since the unit demonstrates the use of helper methods, and I think it is 'best practice', so to speak. Although my Camping form worked perfectly, I could not pass the challenge. 

I eventually landed on the code below, and had no problem to pass this unit. Good Luck. 

1. campingList.cmp

<aura:component implements="force:appHostable">
<ltng:require styles="/resource/SLDS203/assets/styles/salesforce-lightning-design-system.css" />
<!-- CAMPING ITEM DEFINITION -->
<aura:attribute name="newItem" type="Camping_Item__c"
default="{ 'sobjectType': 'Camping_Item__c',
'Name': '',
'Quantity__c': 0,
'Price__c': 0,
'Packed__c': false }" />
<!-- Local Array to hold camping items -->
<aura:attribute name="items" type="Camping_Item__c[]" />

    <!-- FORM TO CAPTURE CAMPING ITEMS -->
<!-- box area -->
<div class="slds-col slds-col--padded slds-p-top--large">
<div aria-labelledby="newcampingitemform">
<fieldset class="slds-box slds-theme--default slds-container--small">
<legend id="newcampingitemform" class="slds-text-heading--small slds-p-vertical--medium">
Add Item
</legend>
<form class="slds-form--stacked">
<!-- CAMPING ITEM NAME FIELD -->
<div class="slds-form-element slds-is-required">
<div class="slds-form-element__control">
<ui:inputText aura:id="name" label="Camping Item Name"
class="slds-input"
labelClass="slds-form-element__label"
value="{!v.newItem.Name}"
required="true" />
</div>
</div>
<!-- PRICE FIELD -->
<div class="slds-form-element">
<div class="slds-form-element__control">
<ui:inputCurrency aura:id="price" label="Price"
class="slds-input"
labelClass="slds-form-element__label"
value="{!v.newItem.Price__c}"
placeholder="0" />
</div>
</div>
<!-- QUANTITY FIELD -->
<div class="slds-form-element">
<div class="slds-form-element__control">
<ui:inputNumber aura:id="quantity" label="Quantity"
class="slds-input"
labelClass="slds-form-element__label"
value="{!v.newItem.Quantity__c}"
placeholder="0" />
</div>
</div>
                    <!-- PACKED FIELD -->
<div class="slds-form-element">
<ui:inputCheckbox aura:id="ispacked" label="Packed"
class="slds-checkbox"
labelClass="slds-form-element__label"
value="{!v.newItem.Packed__c}" />
</div>
<!-- CREATE ITEM BUTTON -->
<div class="slds-form-element__control">
<ui:button label="Create Item"
class="slds-button slds-button--brand"
press="{!c.ClickCreateItem}" />
</div>
                </form>
</fieldset>
</div>
</div>
<!-- ITEM LIST COMPONENT TO SHOW LIST OF ITEMS -->
<div class="slds-card slds-p-top--medium">
<header class="slds-card__header">
<h3 class="slds-text-heading--small">
Camping Items</h3>
</header>
<section class="slds-card__body">
<div id="list" class="row">
<aura:iteration items="{!v.items}" var="item">
<c:campingListItem item="{!item}" />
</aura:iteration>
</div>
</section>
</div>
</aura:component>

2. campingListController.js

({
ClickCreateItem: function (component, event, helper){
var validItem=true;
var nameField = component.find("itemname");
var itemname = nameField.get("v.value");
var priceField = component.find("price");
var price = priceField.get("v.value");
var quantityField = component.find("quantity");
var quantity = quantityField.get("v.value");
       if(($A.util.isEmpty(itemname)) || ($A.util.isEmpty(price)) || ($A.util.isEmpty(quantity))){
validItem = false;
alert("Please fix errors and try again");
nameField.set("v.errors", [{message: "Item Name can't be blank."}]);
priceField.set("v.errors", [{message: "Price can't be blank"}]);
quantityField.set("v.errors", [{message: "Quantity can't be blank"}]);
} else {
nameField.set("v.errors", null);
priceField.set("v.errors", null);
quantityField.set("v.errors", null);
} if(validItem){
var mynewItem = component.get("v.newItem");
var theItems=component.get("v.items");
var newItem = JSON.parse(JSON.stringify(mynewItem));
theItems.push(newItem);
component.set("v.items", theItems);
         //Resetting new item values on the form
component.set("v.newItem",{
'sobjectType': 'Camping_Item__c',
'Name': '',
'Quantity': 0,
'Price': 0,
'Packed__c': false });
}
}
})