My favorite new Salesforce App

      3 Comments on My favorite new Salesforce App

Every org I have worked in has technical debt and general house keeping that needs to be done. Also most orgs I have worked in have little to no documentation. Normally when cleaning up objects I use FieldTrip but recently I found another App called Field Pro. This app differs because not only does it tell you info about how used the field is, it also shows you WHERE the field is used without having to build your own queries against the tooling and metadata API. It also can do quick lookups on single records or do everything on a object by object basis so you can then report on it.

So lets say I find this field and I am can quickly see how much it is populated with a quick report or SOQL query. What I really want to know is in the last year how much is it populated and also where is this used? This is where for me Field Pro SHINES. Go to the App, type in your field, run analysis if it needs to be done (takes no more than 15-30 seconds) and BAM you get this amazing output.

Currently due to limitations of doing this analysis in bulk it doesn’t scan Reports, Process Builder, Flows, or Permission Sets. But look at all this other AMAZING information at my fingertips! Last time the field was scanned, the population percentage in the last whatever time frame you set, then the best at least in my opinion WHERE the field is used and who can see it! This also allows you to do full Object scans and then report off it!

Now lets see how many Custom Fields I have on the Account Object that are Custom and when they were created. This used to be pretty annoying to get at either a manual process or some sort of Metadata API call but now bam!

I can’t give enough praise for how amazing this app is and how much it has helped me. Go give it a go in your org.

Lock Down DLRS Fields and Make DLRS Play Nicely with Validation Rules

So you have a DLRS field and you don’t want your users to update the field as it should always be controlled by DLRS.  This can be taken care of via Field Level Security (FLS).  DLRS Runs in Apex and Apex runs in system mode meaning that it doesn’t care about FLS or anything like that.  This means you can set the field to Read-Only for all profiles and DLRS will still be able to update the field no matter what user triggers the DLRS.

The other bonus to this is you can easily set DLRS fields to bypass Validation Rules.  We now know that the DLRS field can ONLY be updated by a system process (Apex, Workflow Rule, Process Builder, Flow launched from Process Builder) so we can write the following into your validation rules.  NOT(ISCHANGED(DLRS_FIELD__C)), so say the field isnt changed we then end up with NOT(FALSE), this becomes TRUE so your Validation rule fires as it should.  But now when DLRS fires we end up with NOT(TRUE), this becomes False and the validation rule is bypassed so DLRS can keep doing its thing even to records that might be locked by validation rules.

Troubleshooting DLRS 101

      2 Comments on Troubleshooting DLRS 101

Hey long time no help. Sorry about that, M&A’s, new jobs, and babies make for quite a hectic time.  Enough with that onto the good stuff!  Top issues people run into with DLRS

  • First Remote Site Settings Errors! 

Lots of people have noticed that after they spin up a sandbox or have an org migration that their DLRS keeps popping up a warning message along the lines of “Salesforce Metadata API connection failure, click the link below to configure”.

DLRS works with the Metadata API to deploy triggers directly into production from production.  If you regularly deal with deploying apex this is pretty magic in what it does.  The key to this is the remote site settings and the way DLRS does this is it uses a hard-coded value for your cluster.  Downside, if you move clusters or create a sandbox to test DLRS items, etc… The two ways to fix this is delete the remote site setting and let DLRS re-create it or just go into the remote site setting and change the server to the current cluster you are in.  Example is say you are on na2 and you spin up a sandbox and it is cs50.  You’d go into the remote site setting and change it from https://dlrs.na2.visual.force.com to https://dlrs.cs50.visual.force.com and it should start to work again.

  • Missing Child Trigger

You’ve probably tried to save a new DLRS and you get an error that looks something like this.

“Active: Apex Trigger dlrs_contactTrigger has not been deployed. Click Manage Child Trigger and try again.”

If you are using the Custom Metadata you wont even see the Deploy Child Trigger button until the DLRS record is saved so you might be a bit confused on how do you save if there isn’t even a way to deploy!  The easy fix to this is UNCHECK the active box, save the DLRS, deploy the child trigger then edit the DLRS and recheck the active box.

  • Im on DLRS (Enter Not Current Version Here) how do I upgrade?

You can just go to the Git page and install the newest version and it will auto upgrade itself.  If you are more cautious you could uninstall the older version and then update to the newest version.  As always highly recommend installing into sandbox first to make sure nothing breaks 😉

  • Error when deploying child trigger about Object not supporting triggers?

You might see this error when trying to deploy your child trigger you might and an error that looks something like this

Error:
triggers/dlrs_OpportunityCompetitorTrigger.trigger (Line: 4: Column:9) : SObject type does not allow triggers: OpportunityCompetitor

This means that the object doesn’t support triggers and you will need to use a Schedule Calculate or have DLRS Called from Process Builder.  To check if an object can use triggers you can go to workbench then click on the Info and select where your object lives.  Pick your object and click on the attributes folder and look for “triggerable:True”.

Another issue you might run into is the SObject type does not allow insert/delete/update and that means that the object only supports certain type of calls.  The fix for this would be to use Process Builder or modify the DLRS trigger to not fire on the type of call that isnt supported.  To see what is supported for an Object the Developer SOAP guide should have a list of what an object supports.

  • When using DLRS to Count follow this one tip!

Pretty much best practice is to have the Field To Aggregate set to ID.  That seems to fix most counting DLRS issues.

  • What the hell? (Is this broken OR am I doing something wrong)?

So I went to workbench to work on my relationship query and I got it working.

Loads_To__c = ‘Highly Desirable’ OR Loads_From__c = ‘Highly Desirable’

But then when it put that into DLRS I get an error! What the heck?

Error: Relationship Criteria ‘Loads_To__c = ‘Highly Desirable’ OR Loads_From__c = ‘Highly Desirable” is not valid, see SOQL documentation http://www.salesforce.com/us/developer/docs/soql_sosl/Content/sforce_api_calls_soql_select_conditionexpression.htm, error is ‘unexpected token: OR

The answer is any OR statements in DLRS need to be wrapped in parenthesis. So you’d need to write that out like this (Loads_To__c = ‘Highly Desirable’ OR Loads_From__c = ‘Highly Desirable’) then it should work.  Even if you have an OR and then ANDS after it you need to wrap the entire OR statement for DLRS to accept it.

  • Trying to uninstall DLRS and it is telling me triggers still exist! How do I get rid of them?!

Go to your DLRS records and click on the manage child trigger. Remove all the triggers and you should be good to go to uninstall. If you already have removed your DLRS records you can just re-create new ones that point to the same child objects then via the deploy child triggers button you can remove the triggers.  You can also remove them in Sandbox and then use a change-set to remove them from production.  You can also use destructive changesets via workbench if that is a more familiar route.

  • My DLRS isn’t working as expected! Halp!

Don’t worry there are plenty of people willing to offer help in the community.  Drop on by and post and most likely someone can help you figure out whatever the issue is.  You can also post in the GitHub but it is not as active and more for bug reports but that is another avenue you could take.

Mass Delete List View Button in Lightning!

      14 Comments on Mass Delete List View Button in Lightning!

Remember how I said I’d find a way to Mass Delete error records in lightning? Well here it is! (And shown in Lightning as well, you only need to use this for Lightning as you could use the JS Button for Classic)

Step 1 – Download this from Salesforce Labs (https://appexchange.salesforce.com/listingDetail?listingId=a0N300000016YuDEAU)

Step 2 – Clone one of the VF Pages

Step 3 – Replace this in the cloned page with the custom object you want the button to live on

Step 4 – Make new button

Step 5 – Add it to the search results

Step 6 – Mass Delete!

Side note after writing this up I see in 2.9 someone added a VF page button that does the same thing.  WELL THE GOOD NEWS IS THIS WORKS WITH ANY OBJECT IF YOU HAVE ANY OTHER OBJECTS YOU WANNA ADD A MASS DELETE BUTTON TO 😉  The only difference is in editing the VF page you’d put the object API name where we put the DLRS object name and you’d most likely want to name the page something different.

Customer Account Health Indicators

      No Comments on Customer Account Health Indicators

One day I was chatting with one of our Customer Success Managers on HOW he does his job of tracking customers to engage with.  He told me through many reports and talking with our TAM’s and AE’s.  I thought that was silly as while we don’t have a dedicated CSM tool the basics of it didn’t seem to hard to build into Salesforce.

We did a bit of BPR over a delicious Reuben (I would argue is the best sandwich ever made) to pinpoint down some of the key account health indicators.  He came to the conclusion that in our Org these things were key

  • Active Contacts With A Key Role – He saw it as a problem if we had an account with no active contacts with a Key Role set.
  • Days Since Last Activity Logged (Not by an automated system [ex Marketo, Pardot, Outreach.io, Etc…]) – If there had been no logged communication within the last 90 days this was an issue as the AE/CSM/TAM should be reaching out at least once a quarter.
  • Escalated Cases in the last 90 Days – He saw this as a problem if there were more than a set number of Escalated Cases in the last 90 Days.
  • Cases in the last 60 Days – He saw this as a double sided issue, if there were to FEW cases or to MANY cases this could indicate a problem.  No cases as they maybe were not using the product enough or too many cases as they were having issues with the product.
  • Utilization (of our product) – This was how much actual usage / allowed usage.  If this number was too low anytime after 6 months from the start date this would be an issue.
  • Adoption (of our product) – This was user adoption of the platform more or less how many users were using the product.  Too little would show that there were adoption issues and a combination of Utilization and Adoption can show a variety of different issues.

These were all built to be updated in real-time so when one of them flipped it could alert the correct person and once they did what needed to be done they would auto-unflip.  This allowed the CSM to run exception reporting of looking for accounts that had one of these 6 fields out of the range they were looking for, in place of running reports and putting them together in excel trying to FIND accounts that were having issues.  Now the System will find those accounts and mark them for us =)

To solve for the Active Contracts with a Key Role I used DLRS. I created this DLRS to rollup a count to a numeric field, then built a formula field that would be TRUE if that number field was 0 and FALSE if it was above 0.  This allowed for a checkbox to display on the page layout for a simpler experience for the user (Problem or No Problem), but still allowed me to have workflow rules fire off the numeric value that DLRS rolled up.

Then we create the formula field to hold our business logic. In this case it is easy, if it is 0 then we want the box to be checked and if it is any other value we don’t want the box to show as true.

For the days since last activity logged I use process builder to populate the date of the last activity onto the Account.  The trick is we want to exclude all automated activity logs like Marketing Automation or Automated Sales Enablement.

 


Here is where we check to see if the Task was created by an automated system.  We also only want to calculate this for Customer Accounts so we first check to make sure the Account is a Customer.  Then we make sure the record wasn’t created by an Automated System, then we also check to make sure the owner of the task wasn’t an Automated System. Next is to make sure the activity is Closed and that the ActivityDate is less than Jan 1, 3000 this is due to a weird bug with Salesforce for Outlook somehow inserting really weird out of range values so this prevents PB from erroring out when those random records get inserted.

Then for updating of the records we just grab the Account from the Task and stamp a date field with the tasks ActivityDate. In this case the field is named DLRS Last Activity Date as I originally planned to solve this with DLRS but PB ended up being the better tool for this use case.

To make it simple for people to see how many days ago that Actiivity Date was a Formula field is used.

 

The next two are handled with DLRS and they just roll directly into the field we display.

Then we just expose these direct fields as there is no need to mask them. The one thing we have to be sure of is to set up a Schedule Calculate so that way if no new cases or escalations are created in 60 or 90 days the roll-up will re calculate.

The last 2 fields we have created an Object called Utilization and Master-Detailed to Contracts that is then attached to Accounts.   We then via Informatica from our BI Warehouse bring over the calculated Adoption and Utilization values into it.  We then just use DLRS to bring the most recent values onto the Account.

For ease of use I used 2 formula fields to convert the proportion to a percentage as 25% is easier to read than 0.2514.  I also added a long text field for No Longer At Risk Reason this way an account rep or CSM could enter why the account might have at risk factors but not really be at risk then in the reports you can say you only want to pull accounts where that field is Null.

Then the final result ends up looking like this.

Automated Error Alerts and Mass Delete Error Records

I have seen people posting about their scheduled rollups failing and them not finding out about it until later.  Well DLRS has this nice section called Lookup Rollup Summary Logs and it is just a table that holds records of failed scheduled rollups.  This is good news it means we can create a custom report type and then build a exception report so that whenever one of these fails it will trigger and alert us.

First go to Setup and look for Report Types

Then click on New Report Type

Now we need to name the new report type, give it a description and then where we want it to be stored.  I store it under Admin Reports as I am most likely the only person in the Org who will care about DLRS errors 😉  (Pro-Tip make sure you deploy it once you have it made.  You can do that now with the radio buttons at the bottom or you can do it after you save the report type. Just don’t forget to Deploy it!)

Next we only need the main table so we don’t need to set up any table joins so we just click on Save.

Now when we go to Reports we can look for the DLRS Error Report Type

Click to create a new report.  And then build your Report. I keep my report pretty bare bones with just the Log Name so I can go directly to the records.  Feel free to set up as much or little information in your report.  Then I want it to show me ALL records as anytime there are DLRS errors and I fix the problem I clear the table so it doesn’t end up taking up storage space.  If you don’t clear your table you could say that it should only look at records created Yesterday or some specific time frame.

Next is we want to subscribe to the report.

For me I set it up to run every weekday at 8am and if the record count is ever greater than or equal to 1 it will shoot me an email and I will know that one of my scheduled DLRS’s failed.  Click save and you are good to go.

The second part is the two ways I clear my DLRS Error Log Table.  First is I built a small Javascript button (RIP JS Buttons in Lightning.  I think this can be done with Quick Actions but I have not had time to look into it.  I’ll update this post later once I figure out how to do this same trick in a Lightning Friendly way) and added it to the list view of the DLRS Error Page.  To do this you’ll want to go to Setup > Create > Objects and look for Lookup Rollup Summary Log.  Go into the object and scroll down to the buttons and click on create a new button.  Below is the code I use.

{!REQUIRESCRIPT(“/soap/ajax/36.0/connection.js”)}

var url = parent.location.href;

var records = {!GETRECORDIDS($ObjectType.dlrs__LookupRollupSummaryLog__c)};

if (records[0] == null) { //if the button was clicked but there was no record selected

alert(“Please select at least one record to Delete.”); //alert the user that they didn’t make a selection

} else { //otherwise, there was a record selection
alert(“Deleting ” + records.length + ” Records. Press Ok and wait for the page to reload.”);
var result = sforce.connection.deleteIds(records);
parent.location.href = url; //refresh the page
}

Once you have your new button you’ll just need to click on the Search Results under Search Layouts and add the button.  Then it should appear and you can use the button to delete many at a time in a list view.

The other way I clear the table if there are TONS of error log records is export all of the record ID’s with Dataloader then load them back in as a delete command with dataloader.  You can use the Bulk API so it can clear the table pretty quickly.  Until next time when I hope to finish writing up how to create a simple account health warning system with Process Builder, DLRS and some workflow rules.

Counting Tasks with DLRS

      48 Comments on Counting Tasks with DLRS

ObHow many tasks have been done in X time frame is a common ask from marketing or sales.  Before it would require a report to answer but now with DLRS we can have that answer live on the record itself.  In this example I’ll go over Task Counting for Leads.

The theoretical ask this DLRS can solve for is this.  Someone would love to know how many tasks in the last week have been done on a Lead.   Even better they would love to get a quick count of how many tasks are on that Lead that are not from some sort of automated system.  Well with DLRS you can accomplish that pretty easily.  The goal is so an SDR can create a List View that only pulls in leads that have not been contacted by a person more than 2 times in the last week.

First step is on the Lead lets create a numeric field and name it Completed Tasks In The Last Week.  Lets make the API name of the field DLRS_Completed_Tasks_In_The_Last_Week.  Next lets go and create our DLRS.  I’ll use the Custom Metadata layout as that seems to be the new hotness and most likely the future of DLRS.

  1.  This is the Parent Object as we want to roll the count up to the Lead Record
  2. Activities/Task live on the Task Object
  3. WhoID is the field that relates Tasks/Activities to the Lead Object.  It is a strange field here is a data map of it
  4.   This is where all of our business logic will live.  We will go through these step by step for this use case.
    1. For this use case we only want to select Tasks that have been completed.  So we say that IsClosed must be TRUE.
    2. We also don’t want to count ones that our Marketing Automation system creates automatically but WE DO want to count those that are created when a rep sends an email through in this case Marketo Sales Insight.  So we know that when a Rep creates a task via MSI Marketo creates it but ASSIGNS it to the Rep.  When Marketo creates a Task from a marketing email or activity it assigns the task to itself.  In this case the Assigned To field is OwnerID so we say that the OwnerID is NOT EQUAL to the ID of our Marketo user.
    3. We don’t want to count things that were set as complete further in the past then last week so we say ActivityDate >= LAST_WEEK.  We also don’t want to count things in the future where someone might have entered in the wrong value so we use ActivityDate <= TODAY.  We also don’t want to count things where there was no Date filled in so you can’t see it in the screenshot but there is ActivityDate != Null also.  (If you wanted to get fancy you could set up a workflow rule to stamp the date when an activity is completed and use that as your criteria so you could have people leave the due date in the future to show they completed something early).
    4. When we put it all together the field ends up reading as so
      IsClosed = TRUE AND OwnerId != ‘a145000000CDmlQ’ AND ActivityDate >= LAST_WEEK AND ActivityDate <= TODAY AND ActivityDate != Null
  5. This is where we put in all of the fields we use in #4 on separate lines, in this case IsClosed, ActivityDate and OwnerID
  6. We just want a count of the records so we just can put ID here and it will count how many ID’s are returned.
  7. We want to Count how many records are found
  8. This is where we want to store the results on our Lead Record.  In this case we will use the field we just created DLRS_Completed_Tasks_In_The_Last_Week__c
  9. I want this to run in realtime
  10. I want this to bypass any sort of task privacy I might have set up in my org so I have it set to run as System

Once you have this record saved, you can deploy the child trigger via the manage child trigger button then go back and activate the rollup.  Now you will also want to click Calculate to backfill all the records, this might best be done at night as it will take a while to run through all your lead records.  Then you can build a Lead List view for the SDR’s where the DLRS_Completed_Tasks_In_The_Last_Week__c <= 2.  As this is a realtime Rollup that has a date field as a criteria you might want to set up a schedule Calculate to re-run all leads where that value is above 2 every Sunday night so come Monday it is a fresh value for the SDRs to work.

 

Account Based Marketing (ABM) Lite Inside Salesforce

Account Based Marketing (ABM) Lite inside Salesforce

Account Based Marketing (ABM) is the newest and hottest up and coming marketing plans. For a quick read up on what ABM is Salesforce wrote a pretty good blog on ABM. https://www.salesforce.com/blog/2016/12/account-based-marketing-state-of-mind.html

So, the biggest problem that lots of admins will start to face as ABM becomes the next big thing is how the hell do I map leads to accounts?!  This was my solution to the ABM problem inside salesforce, this may not be the most elegant but it works for my needs without having to pay for yet another tool.  It uses a combination of a custom object, in our case Informatica (Could be SFDC Apex/Flow if you wanted), Apex and a handful of custom fields.

First off, the backbone of this is the best way to match Leads to Accounts is NOT on account name, as account name is normally user provided and might not match what we have as the account name in our Account Object.  The answer we came up with is domain matching, you could build some cool fuzzy logic inside Apex but we treat this more as a manual process as some companies have MANY mail domains that don’t match to their web-domain.

The Domain Matching Object is a pretty basic object.  3 custom fields and that’s it.  Simple and clean.  One thing we did do is set up the field that holds the domain to be a unique case insensitive field as for this to work there can only be one domain record.  This is a n:1 type relationship.  We can have many domain records to a single account but we can’t have many accounts to a single domain.

Next the way I populate this object auto-magically is with a scheduled apex class.  But first we must make a few changes to the Lead Object, Contact Object and Account Object.  Let’s get to those and then we will go over my apex code (Disclaimer: I am not a trained developer! The code you will see here is not written using Apex best practices).

Lead Object –

  • Domain Lookup Timestam – Date/Time
  • Email Domain – Formula – Text – SUBSTITUTE(Email, LEFT(Email, FIND(“@”, Email)), NULL)
  • Related Account – Lookup – Account
  • Related Account Type – Formula – Text – Account.Type

Contact Object –

  • Email Domain – Formula – Text – SUBSTITUTE(Email, LEFT(Email, FIND(“@”, Email)), NULL)

Account –

  • New Related List on Page Layout – Domain-Matched Leads

Now back to auto-populating this new object for people to approve or deny matching.  I am not the best at coding so there are most likely ways you can make this better, faster, stronger.  This was the best I had and it does the trick.

If you’ve never built any apex you’ll want to go to Setup > Develop > Apex Classes > New Apex Class.  Then you can copy this as a starting point but you will need to change the fields to the names of the fields in your org.  You can only do this in a sandbox, you can’t write new apex in production.

global class NewAccountDomains implements Schedulable
{
    global void execute(SchedulableContext SC)
    {
        newDomains();
    }
    
    public void newDomains()
    {
        //Create our List of New Account Domain Records to generate at the end.
        List newMatches = new List();
        Set newAddedDomains = new Set();
        
        //First find all of our Customer Account ID's
        Set customerAccountID = new Map<Id, Account>([SELECT Id FROM Account WHERE Type = 'Customer']).keySet();
        
        //Collect a Set of our Domains from the Account Domain Object.  We will use this to exclude in our contact scan
        Set matchedDomains = new Set(); 
        for (Account_Domain_Matching__c domainScan : [Select Id, Domain__c from Account_Domain_Matching__c])
        {
            matchedDomains.add(domainScan.Domain__c);
        }
        
        //Now Collect a List of Contacts that have new Domains that are from Customer Accounts and are not already Matched Domains
        for (Contact newDomains : [SELECT Id, Email_Domain__c, AccountId FROM Contact WHERE Email_Domain__c != NULL AND Email_Domain__c NOT IN :matchedDomains AND AccountId IN :customerAccountID])
        {   
            if(newDomains != null)
            {
                if(!newAddedDomains.contains(newDomains.Email_Domain__c))
                {
                    Account_Domain_Matching__c ADM = new Account_Domain_Matching__c(
                        Domain__c = newDomains.Email_Domain__c,
                        Related_Account__c = newDomains.AccountID,
                        Approved__c = 'Awaiting Approval');
                
                    newMatches.add(ADM);
                    newAddedDomains.add(ADM.Domain__c);
                }
            }
        }
        
        if(newMatches != null)
        {
            insert newMatches;
        }
     }
}

Going over what this class does.  It will grab a list of Accounts that in this case are set as a Customer (you can customize this as you need maybe you only want to grab target accounts, as ABM is a focused way of marketing and selling you will want to limit your account selection else you will end up with a pretty large and mostly impossible domain mapping task.)

It then finds all the current Domain Matching Records we have and the domains that are in those records.  Then it looks at all the contacts attached to those accounts we care about that DO NOT have a domain that is already in our Domain Matching object.  For those that it finds it then creates a new Domain Matching record and sets it to “Awaiting Approval”.

Now you will most likely have a bunch of bad domains in that list.  My buddy Kevin Purdy wrote a small python script that you feed a CSV and it will check to see if the domain responds to a gethostbyname request. If it does not, it writes the domain out to another CSV file.  You can then use this as a primary key in Data Loader and  change the status of those records to rejected.

To use this script you’ll need to install python then copy this code into a new text document and save it as emailscan.py .  Then you’ll want a csv with all of your domains in a single list in the same folder as your emailscan.py.  Then run emailscan.py and it should grab that csv and check if the domains resolve or not, then it will write the ones that don’t resolve to a new folder called results.csv.

 

#!/usr/bin/env python

import socket
import csv
import sys

def hostname_resolves(hostname):
  try:
    socket.gethostbyname(hostname)
    return False
  except socket.error:
    return True

writer = csv.writer(open("results.csv", 'w', newline=""))
with open("emails.csv", "rt") as csvFile:
  reader = csv.reader(csvFile, delimiter=",")
  for line in reader:
    if hostname_resolves(line[0]):
          writer.writerow(line)
          print (line)

I also have this small trigger on the Account Domain Matching object that when a record is denied it clears out the related account so if anything, ever goes wrong and any other processes picks up denied records they at least the record wont map to anything.  Call me paranoid ;) 

To create a trigger go to Setup > Create > Objects > Account_Domain_Matching__c object we created earlier.  Then in this object near the bottom there should be a button to create a new trigger on the object.  You can only do this in a sandbox environment you can’t write apex in production.

trigger NullRelatedAccountOnDeny on Account_Domain_Matching__c (before update, before insert) 
{
    for(Account_Domain_Matching__c adm : Trigger.New)
    {
        if(adm.Approved__c == 'Denied' && adm.Related_Account__c != Null)
        {
            adm.Related_Account__c = Null;
        }
    }

}

(The test below test class covers both the scheduled class and the Null Values trigger.)

Below is the bare min for a test class that will get this covered but by no means follows all best practices.  . Again I’m not a developer by trade, but this will work.  You can use it as a starting point for getting a test class to work in your org at your own level of best practices.

@isTest
public class NewAccountDomainsTest
{
    public static testmethod void testschedule()
    {
        Test.startTest();
        
        //Generate Fakes Data
        
        Account a = new Account(
        type = 'Customer',
        name = 'Testy McTesterson',
        );
        insert a;
        
        Contact c = new Contact(
        firstname = 'Testy', 
        lastname ='McTesty', 
        title ='Magic Maker', 
        email ='Testy@McTesterson.com',
        accountid = a.id
        );
        insert c;
        
        Account_Domain_Matching__c adm = new Account_Domain_Matching__c(
        domain__c = 'Dan.com', 
        approved__c = 'Approved', 
        related_account__c = a.id
        );
        insert adm;

        Account_Domain_Matching__c adm2 = new Account_Domain_Matching__c(
        domain__c = 'Dan2.com', 
        approved__c = 'Denied', 
        related_account__c = a.id
        );
        insert adm2;
                     
        //Setup the Schedule Run
        
        NewAccountDomains nad = new NewAccountDomains();
        String nad1 = '0 00 1 3 * ?';
        system.schedule('Test', nad1, nad);
             
        Test.stopTest();
    }
}

Now we have an object that is full of domain mapping but we don’t have anything matching the leads to those records.  In my case I use a mix of Apex and Informatica.   That Date/Time field we created on the lead object I populate that with a time stamp when leads are created or updated in ways that we would want to re-scan.

Again, I know logic-less triggers are best and a Trigger should just call a class that holds all the logic but, this works for my needs.  You can easily convert this to a logic-less trigger if you are so inclined.

trigger AccountMatchDate on Lead (Before Insert, Before Update) 
{
    if(Trigger.isInsert)
    {
       for(Lead ld : Trigger.New)
       {
          ld.Domain_Lookup_Timestamp__c = datetime.now();
       }
    } 
    else if(Trigger.isUpdate)
    { 
      for(Lead ld : Trigger.New)
      {
        Lead oldld = Trigger.oldMap.get(ld.id);
        if(oldld.Email != ld.Email && oldld.Email_Domain__c != ld.Email_Domain__c)
        {
           ld.Domain_Lookup_Timestamp__c = datetime.now();
        }
      }
    }
} 

Now that we have that date stamp we can use Informatica to do the lookup for us.  If you don’t have informatica you can 100% use Apex or Visual Flow to have this all done in SFDC. In place of a timestamp you would just have Apex or Visual Flow do the lookup on Insert when Email != NULL or when Email Changes.  I don’t have the visual workflow that does that but you COULD.

(Here is a lead trigger that would do this 100% inside sfdc however, I cannot guarantee the efficiency of this code!)

trigger AccountMatch on Lead (Before Insert, Before Update) 
{
    if(Trigger.isInsert)
    {
       List DomainList = new List([SELECT Id, Domain__c, Related_Account__c FROM Account_Domain_Matching__c WHERE Approved__c = 'Approved']);
       Map<String, Account_Domain_Matching__c> DomainMap = new Map<String, Account_Domain_Matching__c>();
       For(Account_Domain_Matching__c adm : DomainList)
       {
           DomainMap.put(adm.Domain__c, adm);
       }    
       
       for(Lead ld : Trigger.New)
       {
          if(ld.Email != Null)
          {
             Account_Domain_Matching__c related = DomainMap.get(ld.Email_Domain__c);
             if(related != Null)
             {
                ld.Related_Account__c = related.Related_Account__c;
             }
          }
       }
    } 
    else if(Trigger.isUpdate)
    { 
       List DomainList = new List([SELECT Id, Domain__c, Related_Account__c FROM Account_Domain_Matching__c WHERE Approved__c = 'Approved']);
       Map<String, Account_Domain_Matching__c> DomainMap = new Map<String, Account_Domain_Matching__c>();
       For(Account_Domain_Matching__c adm : DomainList)
       {
           DomainMap.put(adm.Domain__c, adm);
       }    
      for(Lead ld : Trigger.New)
      {
        Lead oldld = Trigger.oldMap.get(ld.id);
        if(oldld.Email != ld.Email && oldld.Email_Domain__c != ld.Email_Domain__c && ld.Email != Null)
        {
           Account_Domain_Matching__c related = DomainMap.get(ld.Email_Domain__c);
           if(related != Null)
           {
              ld.Related_Account__c = related.Related_Account__c;
           }
        }
      }
    }
}

The way we do it is we have a nightly Informatica task that builds out a csv for all Account Domain Matching records that are set to Approved.  It pulls down the Domain__c and Related_Account__c into a large CSV.  Then we have another task that runs as part of a Task Flow where it grabs the domain off the lead, looks it up off this csv then if a match is found returns the Related_Account__c (this is the ID of the account) to the Lead.Related_Account__c field that we created on the lead object.  To make this a speedier task it will only grab the lead records where that date/time stamp we have is a greater value then the last time the task ran.

Let’s go see what this looks like in action.

Here we have our Account

And our Related Leads

Currently we don’t have any.

This is our Account Domain Matching Record.

Now let’s create a Lead.

Here would be an example where the website wouldn’t be a great option to auto map as an approved mail domain as everyone from Dan Theman Industries has a mail domain of dandonin.com not danthemandonin.com

Same goes for the Account name, we have it as Dan The man Donin Industries but the field we got either from the form or from whatever enrichment database you might use have the account as Dan The Man Industries so without a very fancy and complex fuzzy logic these wouldn’t have been matched correctly.

Now if we go back to our Account we can see this lead is now related.

Now someone who is working on an ABM account they can go to the account and see all Contact and Leads attached and you can make the related list display whatever information you would like.  So, if you have marketing automation fields you can have that display.  This means you can also build workflows and process builder between the objects now.  When a lead comes in and gets a set lead score it could email the ACCOUNT owner aka the person who owns the ABM functions.

This is ABM Lite, hopefully it can help you start to tackle the challenge of ABM inside salesforce!

How to DLRS

      60 Comments on How to DLRS

Salesforce is great for making object creation and data collection super easy.  One thing it doesn’t make so easy is jumping tables as you MUST use a Master Lookup if you want to use Rollup Summary fields and those are even limited in the functions they can do.   You just want to move a field off of a child record onto the parent, or maybe just grab the user name of the last completed activity and write it to the opportunity record that task was related to.  Before it would take some fancy apex or other developer style work to accomplish, UNTIL Andrew Fawcett created this amazing tool called Declarative Lookup Rollup Summaries.  A bit of a mouthful so I always refer to it as DLRS.

This blog is going to be a super broken down step by step of how to DLRS, the most common issues you might run into with it and a few ways I use it.  Pro-Tip don’t try and install this from the app exchange you will find a very out-dated version that no longer installs. So, to install the package head over to the github (https://github.com/afawcett/declarative-lookup-rollup-summaries) and click either Sandbox or Production for the latest version.  I’d recommend giving it a spin in your sandbox first.

Now wait for an Email from SFDC telling you the install has finished.  You should now see a handful of new tabs and a new app called Declarative Lookup Rollup Summaries.Start off by either loading up the app via the app menu or clicking on the welcome tab.  You should see a welcome screen with an error message.  Don’t worry it is just the tool noticing it can’t talk to the SFDC API that does a lot of the heavy lifting for the tool.  To fix this just click on the “Create Remote Site Setting” button.The tool will automatically create what it needs and the screen should reload and look like below.Now there are two ways to use this tool, both work, pick one, stick with it.  If you were to ask Andy he would most likely say to use his cool metadata method because it is slick, smooth, works with change-sets and he also spent a shitload of time making it =).  But in the end either way will work.  The new cool custom metadata way you’ll want to use the Manage Lookup Rollup Summaries tab.  For the old non fancy way you would just use the Lookup Rollup Summaries tab.  If you are brand new to this and don’t have any historic DLRS records best bet is to stick with the new custom metadata way.Loading that up you should see a screen like below.  We will go over what each of the fields means, and what you should enter into them.If you are using the old way it is very similar you’ll just get a record list and will have to select your DLRS record or click on new.

Now using either the Custom Meta Data way or the old record based way the numbers match to the same fields.

  1. Lookup Rollup Summary Name – This is more or less like a field Label.  This is the name of the record for easy people reading.
  2. Lookup Rollup Summary Unique Name – This is like an API name when creating a field.  You will want this to be Unique and use _ in place of spaces.
  3. Parent Object – This is where you will put the API name of the Parent Object.  If using a custom object don’t forget about the __c.  Think of the Parent Object where you want the data to be written to.
  4. Child Object – This is where you will put the API name of the Child Object.  If using a custom object don’t forget about the __c.  Think of the Child Object as the object you want to get the data from.
  5. Relationship Field  – This is the field that holds the ID or PK from the Child to the Parent.  Double check your API names as many times the UI will show say Account but double check in the SOAP API Document. Lots of times it might really be AccountId.  The magic here is that this can just be a text field, a normal lookup field or a master lookup it can work off any of those.  Again if this is a custom field don’t forget the __c =)
  6. Relationship Criteria –  This is where you will write out the WHERE clause of a SOQL query.  We use this to make sure the tool only selects the records we want.  Example say I want to count all of opportunities that have Closed – Won on an account.  There are a few ways to do so but I could go isWon = true or I could say Stage = ‘Closed – Won’.  You can use AND statements here if you have multiple criteria and also use OR statements.  When using an OR statement make sure to wrap the OR statement inside parentheses.
  7. Relationship Criteria Fields – This is where we will want to list each field  that we used in the above Relationship Criteria on a separate line.  We don’t need to copy the logic just need to copy the API Field Names.
  8. Field to Aggregate – This is the API name of the field on the CHILD object that we want to roll-up.
  9. Field to Order By – This field only matters if you are using the Concatenate, Concatenate Distinct, Last and First selection for #10 Aggregate Operation.  By default if you leave this blank it will just use the Field to Aggregate by.  This is a helpful field if you want to select the most recently created record you would put in CreatedDate here so that way the results would be sorted by the CreatedDate field.
  10. Aggregate Operation – We have a few options here.
    • Sum – This will only work on numeric based fields. It takes the sum of your field to aggregate from all of the child records that it finds.
    • Max – This will only work on numeric based fields.  This will select only the highest value of the field to aggregate from all of the child records it finds.
    • Min – This will only work on numeric based fields.  This will select the lowest value of the field to aggregate from all of the child records it finds.
    • Avg – This will only work on numeric based fields. This will take the MEAN of the field to aggregate from all of the child records it finds.
    • Count – This will just do a count of the rows/amount of records returned.  When using Count it is normally best to use the ID field in your Field To Aggregate.
    • Count Distinct – This will do a DISTINCT count of the values in your field to aggregate.  Example is if the returned values are (A, A, A, B, C) in a count it would be 5 but in a count distinct it would only return 3 as there are only 3 distinct values.
    • Concatenate – This will only work on text fields and it will grab the values in your field to aggregate and place them value after value into the field you want.  You’ll want to use #17 to set how you want the tool to break these values apart either with a comma or a dash etc…
    • Concatenate Distinct – This works the same as above but again just like a Distinct Count it will only grab the distinct values.  So if we had (Dan, Dan, Dan, Not Dan) with just Concatenate we would return in a text field “DanDanDanNotDan” but with Concatenate Distinct we would only return “DanNotDan”.
    • First –  This will work with any type of field.  This will grab the FIRST record based on how the results are sorted then take that field to aggregate from that record.
    • Last –  This will work with any type of field and does the same as above expect it grabs the LAST record then will take the field to aggregate from that record.
  11. Aggregate Result Field – This is the field on the PARENT object where you want the results to be written.
  12. Aggregate All Rows –  This will include records that might be in the recycle bin or are archived by the system.  If this box is not checked then records that have been archived or are in the recycle bin will not be counted.
  13. Row Limit – This only works with the Last and Concatenate Operations but it will let you define a set row count.  Say you only wanted to grab the last 5 records you would use the Last Operation and set this to 5.
  14. Active –  This is what activates the DLRS record or not.  If this box is not checked DLRS will not apply this rollup function.
  15. Calculation Mode
    • Realtime – Happens in real-time.  When someone makes a change that would trigger DLRS to run, it will calculate the roll-up and change the result on the parent if the result is different than what is already there.  You must deploy the child apex trigger for this mode to work.
    • Scheduled – This mode will start to collect records at the bottom of each roll-up under the “Lookup Rollup Summary Schedule Items”.  You will have to schedule apex to run on whatever cadence you would like by having the system run the Apex Class RollupJob.  When this Apex Class runs it will collect all of your Lookup Rollup Summary Schedule Items and process them, then calculating the rollups and updating the parent records. You must deploy the child apex trigger for this mode to work.
    • Developer –  This mode allows you to call DLRS from your own Triggers/Classes.
    • Process Builder – This mode allows you to call a DLRS record from inside Process Builder.  When using this mode you DO NOT need to deploy the child apex trigger.
  16. Calculation Sharing Mode
    • User – The user that triggers the DLRS to be run, the lookup will respect system sharing rules and only calculate the rollup based on what that user has access to.
    • System – This acts like Apex or Workflow Rules by running in system and has visibility into all records.  Rollups will be calculated based on all child records regardless if the user who triggered the DLRS has the permissions to see all records.
  17. Concatenate Delimiter – This is how you set how you want your concatenated operation to be split.  You can use a , or a | or a line break BR().
  18. Description – This is where you can leave notes for yourself on the purpose of the DLRS record, what it should be doing and why so that way the next admin who comes across this won’t be totally lost.
  19. Test Code –  If you have custom validation rules or apex in your org that has the manage child trigger running into issues deploying the trigger, you can type in custom test code here.

Now after you save your new DLRS record you will see some new buttons.Now I am going you assume you understand the Delete and Save buttons (You’ll see Edit if you are using the old method).

  • Manage Child Trigger is how you can deploy and remove the apex code that this tool needs to run.  You only need to deploy this one time per child object that you have DLRS running on.  Example if you have 3 DLRS records all running on Opportunity as the child object you need only deploy the trigger one time.
    It might take a bit of time to deploy, just wait young grasshopper, soon you shall be rolling up fields like a pro. Once it is done you should see this
    at the top of the page.
  • Calculate this button will do an entire org recalculation of the dlrs record.  This is great for back filling parent records after you create a new DLRS record.

    This text box allows us to apply some PARENT object filters.  Say we are running a recalculate on a DLRS and the Account object was the parent object.  But we only want this back-calculation to be done on Customer Accounts.  We could put a WHERE clause here like Type = ‘Customer’ then when it does a system recalculate it will only do so for parent records that meet that criteria.  Calculate Jobs are run as Apex Jobs and you can find the status of them in the Apex Jobs section of Setup.
  • Schedule Calculate this one is a tricky one.  It more or less will automate going into the DLRS record and pressing that Calculate button for you on a set schedule.  In those cases where you have time or date values as part of the relationship criteria you will normally want to force a system recalculation every now and then.
    Here you can also enter in a Parent Object where clause, and then pick the frequency and time you would like this to run.  Once you press Schedule Recurring Calculate Job you can find the job under Scheduled Apex.

Now lets take a look at a sample request someone might have and a real world solution on how to use DLRS.  We will not only look at the request, how to build the DLRS we will also try some SOQL queries out to understand how the DLRS tool works.

If you live in the SaaS world then you love to know when your customers might be at risk of churn.  One of the signs that a customer account might be at risk is you have no data on customer contacts at the company.  Not only do we want to know if we have contacts we want those contacts to have contact roles so we know that if we do have contacts on an account we have a few critical ones that are important when we need to reach out.

In this case we can create a DLRS that will look at all the contacts on an account and see if they are the type of contacts we want then roll a count of how many of those contacts we have onto the Account.  We can then do all the fun Salesforce stuff like build a workflow email off that field where if the value is too low it can alert the account owner or a CSM.  Below is an example of how we would build this.One thing you might notice is the Aggregate Result Field I have the start of the field name DLRS_.  I would HIGHLY recommend naming all the fields you plan to roll values into with DLRS somewhere in the field so other admins or later in life you know that there is apex that relies on that field and it should not be deleted or manually modified.

We set our Parent Object to Account and our Child to Contact.  We know that the field on the Contact that holds the Account relationship is called AccountId.  How did we know that? We double checked here. Then we know we want to make sure that we are only looking at customer accounts, but DLRS is limited to relationship criteria on the child record only.  So I created a formula field that brings down the Account.Type onto the contact record.  Pro-Tip changes to Formula fields don’t cause a record edit so if this Account became a non-customer that would not cause the DLRS to re-fire and re-calculate the field on the parent.

Next we want to make sure the contact record isn’t old and they are still with their company.  Then we have a list of contact roles that are important to use.  So we build up the WHERE statement of
(No_Longer_With_Comapny__c != TRUE AND Contact_Role__c INCLUDES ('Operations Contact', 'External Community Manager', 'Internal Community Manager', 'Primary Contact') AND Account_Type__c = 'Customer')
Thats a big statement, first if you are not familiar with != it is the same thing as NOT EQUALS or <>.  Includes is there because Contact_Role__c is a multi-select picklist so we need to just see if that text is anywhere in the field.  Then Account_Type__c = ‘Customer’ is because we don’t need this DLRS to run unless they are a customer.  Now lets see how this works from a SOQL point of view.

If you have never opened the Developer Console click on your name in the top right and you should be able to select Developer Console. A small popup window should appear, in the bottom area you’ll want to select Query Editor.  Then lets create our SOQL query.  We already have our WHERE in our Relationship Criteria.  Now for the start of it lets put

SELECT [Field we want to rollup cept without the brackets] FROM [Child Object] WHERE [Relationship Criteria] AND [Relationship Field] = '[Id of a parent record to use for testing]'
In our case it looks like this
SELECT No_Longer_With_Company__c, Contact_Role__c, Account_Type__c FROM CONTACT WHERE (No_Longer_With_Company__c != TRUE AND Contact_Role__c INCLUDES ('Operations Contact', 'External Community Manager', 'Internal Community Manager', 'Primary Contact') AND Account_Type__c = 'Customer') and AccountId = '0015000000os367'

This is what we get when we press the execute button in the bottom left.Now in this case we are just using a count so it will just count the total rows.  In this case we can see the results have a total rows of 2, so in theory our DLRS results field should show 2.  This is a great way of checking if the issue is with DLRS as a tool, your data, or your SOQL query.

In this case I then built a formula field that returns a checkbox and if the DLRS Result field of IF(DLRS_At_Risk_Contact_Count__c > 1, False, True).  This way if we have more than 1 result it wont mark the account at risk for low contact count and if it is below 2 then it will mark the checkbox that fires off some workflow rules.

With that you should be more than ready to start building your own DLRS records!  If you have any issues come to the DLRS community and plenty of people will be more than willing to help you troubleshoot any issues and problem solve on creative solutions to issues you might be having with the tool.