Month: July 2017

SOSL Queries in Salesforce

Salesforce Object Search Language (SOSL) is a Salesforce search language that is used to perform text searches in records. Use SOSL to search fields across multiple standard and custom object records in Salesforce.

This is an example of a SOSL query that searches for accounts and contacts that have any fields with the word ‘SFDC’.

List searchList = [FIND ‘SFDC’ IN ALL FIELDS                                                                                                         RETURNING Account(Name), Contact(FirstName,LastName)];

SOSL Syntax –

FIND ‘SearchQuery‘ [IN SearchGroup] [RETURNING ObjectsAndFields]

SearchQuery is the text to search for (a single word or a phrase). Search terms can be grouped with logical operators (AND, OR) and parentheses. Also, search terms can include wildcard characters (*, ?). The * wildcard matches zero or more characters at the middle or end of the search term. The ? wildcard matches only one character at the middle or end of the search term.

Text searches are case-insensitive. For example, searching for Customer, customer, or CUSTOMER all return the same results.

SearchGroup is optional. It is the scope of the fields to search. If not specified, the default search scope is all fields. SearchGroup can take one of the following values.

  • ALL FIELDS
  • NAME FIELDS
  • EMAIL FIELDS
  • PHONE FIELDS
  • SIDEBAR FIELDS

ObjectsAndFields is optional. It is the information to return in the search result—a list of one or more sObjects and, within each sObject, list of one or more fields. If not specified, the search results contain the IDs of all objects found.

Using SOSL in Apex


List<List<sObject>> searchList = [FIND 'Wingo OR SFDC' IN ALL FIELDS 
                        RETURNING Account(Name),Contact(FirstName,LastName,Department)];
Account[] searchAccounts = (Account[])searchList[0];
Contact[] searchContacts = (Contact[])searchList[1];

System.debug('Found the following accounts.');
for (Account a : searchAccounts) {
 System.debug(a.Name);
}

System.debug('Found the following contacts.');
for (Contact c : searchContacts) {
 System.debug(c.LastName + ', ' + c.FirstName);
}

This SOSL query combines two search terms by using the OR logical operator—it searches for Wingo or SFDC in any field. This example returns all the sample accounts because they each have a field containing one of the words. The SOSL search results are returned in a list of lists. Each list contains an array of the returned records. In this case, the list has two elements. At index 0, the list contains the array of accounts. At index 1, the list contains the array of contacts.

Using Filter, Reorder and Limit in SOSL 

You can use WHERE clause, ORDER BY, and LIMIT the returned results of a SOSL query. Because SOSL queries can return multiple sObjects, those filters are applied within each sObject inside the RETURNING clause.

For example, this results in only accounts whose industry is Apparel to be returned:     RETURNING Account(Name, Industry WHERE Industry=’Apparel’)

For example this causes the returned accounts to be ordered by the Name field:             RETURNING Account(Name, Industry ORDER BY Name).

This example limits the returned accounts to 10 only:                                                           RETURNING Account(Name, Industry LIMIT 10)

Validation Rule to Prevent Deletion of Child in Master-Details

You have 2 custom objects, Job Applications and Reviews that have a master-detail relationship. Users should NOT be allowed to delete review records after job application records have been approved. How would a developer meet this requirement?

Ans – Use a validation rule in conjunction with a roll-up summary field

Validation rule will fire only on Insert or Update operation but not on Delete. But in Master-Details when you have a roll-up summary field, the delete in Child will be update the Master and will fire the Validation rule in Master object.

Validation Rule –

AND( PRIORVALUE( Number_of_Reviews__c ) > Number_of_Reviews__c , TEXT(Picklist__c ) == ‘Approved’ )

If you delete the Child record(i.e. the review record) it will delete the record and will initiate the rollup summary field calculation in the Master object record(i.e Job Application) which is an update in Master, so it will fire the Validation  rule of Master object and will prevent the child record to be deleted from the system. This all operation happen in a single execution.

You can follow the Execution order as below –

1. Old record loaded from database (or initialized for new inserts)
2. New record values overwrite old values
3. System Validation Rules (If inserting Opportunity Products, Custom Validation                          Rules will also fire in addition to System Validation Rules)
4. All Apex before triggers (EE / UE only)
5. Custom Validation Rules
6. Record saved to database (but not committed)
7. Record reloaded from database
8. All Apex after triggers (EE / UE only)
9. Assignment rules
10. Auto-response rules
11. Workflow rules
12. Processes
13. Escalation rules
14. Parent Rollup Summary Formula value updated (if present)
15. Database commit
16. Post-commit logic (sending email)

Previous load of class failed: in Salesforce

line -1, column -1: Previous load of class failed: – In most scenarios, this error message is related to the “IsValid”  checkbox for the code which is being referred to in the message.

“IsValid” Checkbox is unchecked for an Apex Class or a Trigger when the metadata on which the code is dependent, has been changed and the code is not compiled after that. The code can be compiled by running the Run All Test, or clicking “Compile All Classes” link, or by Editing and saving that class, or by executing the code in the class. When the code is compiled, either an error message is displayed which can be rectified, or the code is saved successfully and the isValid checkbox is checked.

Invalid Class/Trigger will cause this issue in the org. You can query and check if any invalid classes or triggers in you org from ApexClass and ApexTrigger like below.

Select Id, IsValid, Name From ApexClass where isValid=false

Select Id, IsValid, Name From ApexTrigger where isValid=false

Apex classes are stored with an isValid flag that is set to true as long as dependent metadata has not changed since the class was last compiled. If any changes are made to object names or fields that are used in the class, including superficial changes such as edits to an object or field description, or if changes are made to a class that calls this class, the isValid flag is set to false. When a trigger or Web service call invokes the class, the code is recompiled and the user is notified if there are any errors. If there are no errors, the isValid flag is reset.

Triggers are stored with an isValid flag that is set to true as long as dependent metadata has not changed since the trigger was last compiled. If any changes are made to object names or fields that are used in the trigger, including superficial changes such as edits to an object or field description, the isValid flag is set to false until the Apex compiler reprocesses the code. Recompiling occurs when the trigger is next executed, or when a user re-saves the trigger in metadata.

Resolution –  To resolve this error recompile all classes and do the changes if any class failed to compiled as per the error message.

Steps To Compile All Classes .

Setup > Apex Classes  – Click on the “Complie all Classes” link as below.

CompleClass

Quote Salesforce

Quotes in Salesforce represent the proposed prices of your company’s products and services. You create a quote from an opportunity and its products. Each opportunity can have multiple associated quotes, and any one of them can be synced with the opportunity. When a quote and an opportunity are synced, any change to line items in the quote syncs with products on the opportunity, and vice versa.

Create and Manage Quote –

Create a set of quotes to show different combinations of products, discounts, and quantities so customers can compare prices. Then add products as needed to keep your quotes up to date.

  1. You can create Quote from quote related list on an opportunity.
  2. On Quote creation –
    1. A unique quote number is added.
    2. Products on the opportunity are copied to the quote as line items.
    3. The grand total is recalculated based on the taxes and shipping information that you entered.
  3. Considerations for Creating and Managing Quotes and Quote Line Items.

    1. Relevant price books, products, and list prices must be active in an opportunity before you can create a quote for the opportunity.
    2. If a quote is synced with an opportunity, and you add a line item to the quote, the new line item is copied to the opportunity as a product in the Products related list.
    3. If an opportunity is syncing with a quote, deleting the quote line item deletes the corresponding product from the opportunity’s Products related list.
    4. Custom field syncing isn’t supported. When a quote is created from the API, the quote line items aren’t created based on the opportunity line items. New opportunity line items are created based on the quote line items.
    5. If a product has default schedules, the schedules aren’t created when you add a line item to a quote. If the line item is copied from the opportunity, you can view the schedule from the opportunity. To change the schedule, edit the product record directly.
    6. If you have a quote line item with a discount, and the product has a default schedule on the product record or an individual schedule on the opportunity product record, you can’t edit the Discount field of the line item.
Sync Quotes and Opportunities

To Sync a quote with Opportunity, open quote details page and click Start Sync.

QuoteSync

To stop syncing between a quote and an opportunity, open the synced quote, and then click Stop Sync.

Use of Quote Sync –

Quote syncing lets you link a quote to the opportunity that it was created from and synchronize all updates between the two records. An opportunity can have multiple quotes, but it can sync with only one quote at a time.

  • While a quote and an opportunity are synced, any addition or change to the list of products in one record syncs with the list of products in the other one.
  • Adding or removing a line item from a quote updates the synced opportunity’s Products related list.
  • Adding or removing a product from the opportunity updates the synced quote’s Quote Line Items related list.
  • Product sorting also syncs between the two records.
  • The quote and opportunity continue to sync each way until you stop syncing or delete one of the records.
  • You can sync quotes and opportunities that don’t have any products. When you add a product to either record, it’s automatically added to the record that it’s synced to.
Troubleshooting Quote Syncing
  • This quote can’t be synced because it has inactive or archived products.
    • If the product isn’t active, edit the product and select the Active checkbox.
    • If the product has been archived and the opportunity isn’t closed, delete the product.
  • This quote can’t be synced because it has an inactive or archived price book.
    • If the price book isn’t active, edit the price book and select the Active checkbox.
    • If the price book has been archived and the opportunity isn’t closed, delete the price book.
  • This quote can’t be synced because it has inactive or archived list prices.
    • If the list price isn’t active, edit the list price and select the Active checkbox for the list price.
    • If the list price has been archived and the opportunity isn’t closed, delete the list price.
  • This quote can’t be synced because it has an inactive currency.

Activate the currency from Setup by entering Manage Currencies in the Quick Find box, then selecting Manage Currencies.

  • This quote can’t be synced because one or more of the schedules for the opportunity products have changed since the quote was created.

Create a different quote and sync it, or open the product, delete the schedule, and then re-create the schedule.

  • This quote can’t be synced because another quote that’s being synced for this opportunity is locked due to a workflow approval process. Opportunities can only sync with one quote at a time.
    To unlock the other quote, finish the approval process or recall the request for approval.
  • The SyncedQuote field is read only within a trigger.

Determine whether your Salesforce org is using an Apex trigger that’s attempting to modify the SyncedQuote field. The SyncedQuote field is read only and can’t be modified with a trigger. An admin can modify the trigger.

Email Quote PDF

Provide quotes to your customers the easy way: via PDF. Create quote PDFs from a standard template or from your company’s design. Email quotes to your customers. Then delete quotes that you no longer need.

Create a Quote PDF from the Standard Template-
  1. On the quote detail page, generate a preview by clicking Create PDF.
  2. Save the PDF to the Quote PDFs related list by clicking Save to Quote. The PDF name is the quote name plus a version number, such as AcmeQuote_V1
Create a Quote PDF by Selecting a Template

Before using the Template to create PDF, create a Template in Quote, to make it available.

Then Create PDF will show as dropdown list, which allow to select the custom created template.

Email a Quote PDF in Salesforce Classic.

You can email the Quote PDF from Email Quote button of Quote details page, this will attached the latest version of PDF created.

You can send the individual Quote PDF from Quote PDFs related list of Quote.

Considerations for Deleting Quotes

Before you delete a quote, it’s a good idea to understand how deleting a quote affects quote-related PDFs and syncing with opportunities.

  • An admin, the quote owner, or someone above the quote owner in the organization role hierarchy, you can delete quotes.
  • When you delete a quote, All related PDFs, notes, and attachments are deleted. If the quote is synced with an opportunity, the record is removed from the opportunity’s Quotes related list.
  • If you undelete a previously synced quote, it’s no longer synced with the opportunity.

Retrieving sObject RecordTypeId in Apex

Usually we required to get the sObject record type id when working in Apex. There are different ways to get the record type Id in Salesforce.

We can query from RecordType object to get the record type type id of the object, like below syntax-

RecordType rt = [SELECT Id FROM RecordType WHERE SObjectType = ‘OBJECT NAME‘ AND DeveloperName = ‘RECORDTYPE NAME‘ LIMIT 1];

RecordType accRecTypeId = [Select id from RecordType where sObjectType = ‘Account’ and DeveloperName =’Person Account’ ];

This works fine, but it will use one query against our query governor limit which may create issue in different scenario.

Instead of SOQL query, its preferable to use dynamic apex to get RecordTypeId. Here is the way to do this without any query:

Id accRecordTypeId = Schema. SObjectType. Account. getRecordTypeInfosByName().                                                                                         get(‘Person Account’).getRecordTypeId();

Best way to access record type id using Schema which does not count in Salesforce SOQL governors limit. In below code, instead of hard coding the object name and record type label, dynamic approach which give flexibility to admin or developer.

 public class ObjectUtility {  
   public static Map<String,Schema.RecordTypeInfo> recordTypeInfoMap = new Map<String,Schema.RecordTypeInfo>();  
   public static Id getObjectRecordTypeId(String sObjectName, String recordTypeName) {  
     sObject obj;  
     Schema.SObjectType sObjectType = Schema.getGlobalDescribe().get(sObjectName);  
     if(sObjectType != null) {  
       obj = sObjectType.newsObject();  
       Schema.DescribeSObjectResult sObjDesc = obj.getSObjectType().getDescribe();  
       if(sObjDesc != null) {  
         recordTypeInfoMap = sObjDesc.getRecordTypeInfosByName();  
       }  
       if(!recordTypeInfoMap.containsKey(recordTypeName)) {  
         throw new RecordTypeException('Record type "'+ recordTypeName +'" does not exist.');  
       } else {  
         return recordTypeInfoMap.get(recordTypeName).getRecordTypeId();  
       }  
     }  
     return null;  
   }  
   public class RecordTypeException extends Exception{}  
 }  

Now you can retrieve SObject RecordType Id without any queries using the following code:

Now pass parameters to above method. Parameters are: Object name and Record Type label. Do not pass developer name.

ID RecordTypeId = ObjectUtility.getObjectRecordTypeId(‘Account’ , ‘PersonAccount’);

Salesforce Object Relationship

Relationships associate objects with other objects.

You can define different types of relationships by creating custom relationship fields on an object. The differences between relationship types include how they handle data deletion, record ownership, security, and required fields in page layouts.

Master-Detail (1:n) – A parent-child relationship in which the master object controls certain behaviors of the detail object:

  • When a record of the master object is deleted, its related detail records are also deleted.
  • The Owner field on the detail object is not available and is automatically set to the owner of its associated master record. Custom objects on the detail side of a master-detail relationship cannot have sharing rules, manual sharing, or queues, as these require the Owner field.
  • The detail record inherits the sharing and security settings of its master record.
  • The master-detail relationship field is required on the page layout of the detail record.
  • By default, records can’t be reparented in master-detail relationships. Administrators can, however, allow child records in master-detail relationships on custom objects to be reparented to different parent records by selecting the Allow reparenting option in the master-detail relationship definition.
  • You can define master-detail relationships between custom objects or between a custom object and a standard object.
  • However, the standard object cannot be on the detail side of a relationship with a custom object.
  • In addition, you cannot create a master-detail relationship in which the User or Lead objects are the master.
  • When you define a master-detail relationship, the custom object on which you are working is the detail side. Its data can appear as a custom related list on page layouts for the other object.

Many-to-many —You can use master-detail relationships to model many-to-many relationships between any two objects. A many-to-many relationship allows each record of one object to be linked to multiple records from another object and vice versa.

For example, you create a custom object called “Bug” that relates to the standard case object such that a bug could be related to multiple cases and a case could also be related to multiple bugs.

To create a many-to-many relationship, simply create a custom junction object with two master-detail relationship fields, each linking to the objects you want to relate.

Lookup (1:n) — This type of relationship links two objects together, but has no effect on deletion or security. Unlike master-detail fields, lookup fields are not automatically required.When you define a lookup relationship, data from one object can appear as a custom related list on page layouts for the other object.

Setting DML Option in Apex

You can specify DML options for insert and update operations by setting the desired options in the Database.DMLOptions object.

The Database.DMLOptions class has the following properties:

  • allowFieldTruncation Property [truncation behavior of fields]
  • assignmentRuleHeader Property [Assignment rule information]
  • dupicateRuleHeader [Duplicate rule information]
  • emailHeader Property [Whether automatic emails are sent]
  • localeOptions Property [The user locale for labels]
  • optAllOrNone Property [Whether the operation allows for partial success]

1. allowFieldTruncation Property

The allowFieldTruncation property specifies the truncation behavior of strings.  If you specify a value for a string and that value is too large, the value is truncated in API version below 15.

For API version 15.0 and later, if a value is specified that is too large, the operation fails and an error message is returned. The allowFieldTruncation property allows you to specify that the previous behavior, truncation, be used instead of the new behavior in Apex saved against API versions 15.0 and later.

If allowFieldTruncation  value is true, the property truncates String values that are too long.

Database.DMLOptions dml = new Database.DMLOptions();                                                         dml.allowFieldTruncation = true;

2. assignmentRuleHeader Property

The assignmentRuleHeader property specifies the assignment rule to be used when creating a case or lead.

Using the assignmentRuleHeader property, you can set these options:

  • assignmentRuleID: The ID of an assignment rule for the case or lead. The assignment rule can be active or inactive. The ID can be retrieved by querying the AssignmentRule sObject. If specified, do not specify useDefaultRule. If the value is not in the correct ID format (15-character or 18-character Salesforce ID), the call fails and an exception is returned.

Database.DMLOptions dmo = new Database.DMLOptions();
dmo.assignmentRuleHeader.assignmentRuleId= '01QD0000000EqAn';

Lead l = new Lead(company='ABC', lastname='Smith');
l.setOptions(dmo);
insert l;

  • useDefaultRule: Indicates whether the default (active) assignment rule will be used for a case or lead. If specified, do not specify an assignmentRuleId.

Database.DMLOptions dmo = new Database.DMLOptions();
dmo.assignmentRuleHeader.useDefaultRule= true;

Lead l = new Lead(company='ABC', lastname='Smith');
l.setOptions(dmo);
insert l;

3. dupicateRuleHeader Property

The dupicateRuleHeader property determines whether a record that’s identified as a duplicate can be saved. Duplicate rules are part of the Duplicate Management feature.

Using the dupicateRuleHeader property, you can set these options.

  • allowSave: Indicates whether a record that’s identified as a duplicate can be saved.

Database.DMLOptions dml = new Database.DMLOptions();
dml.DuplicateRuleHeader.AllowSave = true;
Account duplicateAccount = new Account(Name='dupe');
Database.SaveResult sr = Database.insert(duplicateAccount, dml);
if (sr.isSuccess()) {
System.debug('### - Duplicate account has been inserted in Salesforce!');
}

4. optAllOrNone Property

The optAllOrNone property specifies whether the operation allows for partial success. If optAllOrNone is set to true, all changes are rolled back if any record causes errors. The default for this property is false and successfully processed records are committed while records with errors aren’t.