Showing posts with label spring20. Show all posts
Showing posts with label spring20. Show all posts

Saturday, 8 February 2020

Spring 20 Default Record Create Values

Spring 20 Default Record Create Values


Introduction


Spring 20 introduces something rather reminiscent of URL hacks - the capability to provide default values when creating a record via URL parameters.  Not quite the same though -  for one thing this is a supported mechanism, whereas we were constantly warned against using URL hacks in Classic by Salesforce. For another, this is currently only documented as working on record create, whereas URL hacks worked (although wasn't supported, might not work in the future etc) across most of the later Classic pages.

How It's Done


The first thing you need is the URL to the create page - this takes the form:

https://<salesforce_instance>/lightning/o/<object_api_name>/new

e.g. for Account this is :

https://<salesforce_instance>/lightning/o/Account/new

While for a custom object with an api name of Webinar__c it is:

https://<salesforce_instance>/lightning/o/Webinar__c/new

Next, you need the parameter the defines the defaults:

?defaultFieldValues=
and then the list of name=value pairs for the fields - again, these are the API names, so the record name would be:

Name=Test
while a custom field would have the API name, including the __c suffix:

Description__c=Test+record+for+blog
(note that I've used '+' to indicate a space in the query part of the URL - I could just as well have used '%32')

Multiple Values

To add multiple default parameter values, use the comma ',' character to separate them - don't use '&' as this will indicate the defaultFieldValues parameter has finished and a new parameter is starting!

Name=Test,Description__c=Test+record+for+blog,

All Together Now


Putting all the elements identified above, I have the following URL:


https://kabdev.lightning.force.com/lightning/o/Webinar__c
/new?defaultFieldValues=Name=Test,
Description__c=Test+record+for+blog,Planned_Duration__c=60

Entering this in my Spring 20 org takes me to the record create page for the Webinar custom object, with the defaults pre-populated:



Relationship Fields

 MyWebinar__c custom object has a lookup to a custom survey object, with the relationship field named Survey__c. When entering information on the create page, I put the text name of the field and choose the entry from the list, so it's tempting to specify the name of the record in the URL. That doesn't end well:



Leaving aside the frankly hilarious idea that I could take an internal error id from Salesforce to my administrator and that would help in any way, it clearly doesn't like the text. This is because the relationship field needs an ID, so if I specify one of those it populates correctly.



Out of curiosity I tried an non-existent ID to see if the error message is any more helpful - it is, and tells me that it can't find the record that the ID refers to, which is much better.


I must also say that in both error cases I love that the plucky Save button sticks around even though things have gone badly wrong. Clicking it doesn't do anything, in case you are wondering.


Related Posts






Friday, 31 January 2020

Spring 20 Before Save Flows and Apex Triggers

Spring 20 Before Save Flows and Apex Triggers


Introduction

Spring 20 introduces the concept of the before record save flow, analogous to the before insert/update trigger that we’ve had for over a decade now. Like these triggers, the flow can make additional changes to the records without needing to save it to the database once it has finished its work. Avoiding this save makes things a lot faster - a claimed 10 times faster than doing similar work in process builder. What the release notes don’t tell us is how they compare to Apex triggers, which was the kind of thing I was a lot more interested in.


Scenarios

I've tried a couple of relatively simple scenarios, both of which I've encountered in the real world:
  1. Changing the name of an opportunity to reflect things like the amount, close date. All activity happens on the record being inserted, so this is very simple.
  2. Changing the name of an opportunity as above, but including the name of the account the opportunity is associated with, so an additional record has to be retrieved.
In order to push the trigger/flow to a reasonable degree, I'm inserting a thousand opportunities which are round robin'ed across two hundred accounts and immediately deleting them.

Scenario 1


Flow

My flow is about as simple as it gets:



The assignment appends '-{opportunity amount}' to the record name:


At the end of the transaction, I have the following limit stats:

Number of SOQL queries: 2 out of 100
Number of query rows: 1219 out of 50000
Maximum CPU time: 116 out of 10000


Trigger

The trigger is also very simple:

trigger Opp_biu on Opportunity (before insert, before update) 
{
    for (Opportunity opp : trigger.new)
    {
        opp.name=opp.Name + '-' + opp.Amount;
    }
}

and this gives the following limit stats:

Number of SOQL queries: 2 out of 100
Number of query rows: 1219 out of 50000
Maximum CPU time: 1378 out of 10000

So in this case the trigger consumes over a thousand more milliseconds of CPU time. Depending on what else is going on in my transaction, this could be the difference between success and failure.


Scenario 2


Flow

There's a little more to the flow this time :


The Get Account element retrieves the account record associated with the opportunity - I only extract the Name field as that is all I use in my opportunity name: 


I also have a formula that generates the opportunity name, and this is used by the Assignment action:

and this gives the following limit stats:

Number of SOQL queries: 7 out of 100
Number of query rows: 2219 out of 50000
Maximum CPU time: 111 out of 10000


Trigger

The trigger mirrors the flow, with a little extra code to ensure it is bulkified :

trigger Opp_biu on Opportunity (before insert, before update) 
{
    Set<Id> accountIds=new Set<Id>();
 for (Opportunity opp : trigger.new) 
    {
        accountIds.add(opp.AccountId);
    }
    
    Map<Id, Account> accountsById=new Map<Id, Account>(
        [select id, Name from Account where id in :accountIds]);
    for (Opportunity opp : trigger.new)
    {
        Account acc=accountsById.get(opp.AccountId); 
        opp.Name=acc.Name + '-' + opp.CloseDate + '-' + opp.Amount;
    }
}

which gives the following limit stats:

Number of SOQL queries: 7 out of 100
Number of query rows: 2219 out of 50000
Maximum CPU time: 1773 out of 10000

Aside from telling us that CPU time isn't an exact science, as it went down this time, the flow is pretty much the same in spite of the additional work. The trigger, on the other hand, has consumed another 500 milliseconds.


All Flow All the Time?

So based on this, should all before insert/update functionality be migrated to flows? As always, the answer is it depends.

One thing it depends on is whether you can do everything you need in the flow - per Salesforce Process Builder best practice:

For each object, use one automation tool.

If an object has one process, one Apex trigger, and three workflow rules, you can’t reliably predict the results of a record change.

It can also get really difficult to debug problems if you have your business logic striped across multiple technologies, especially if some aspects of it are trivial to change in production.

Something that is often forgotten with insert/update automation is what should happen when a record is restored from the recycle bin. In may ways this can be considered identical to inserting a new reecord. Triggers offer an after undelete variant to allow automated actions to take place - you don't currently have this option in the no code world.


One More Thing

A word of warning - you might be tempted to implement your next simple before save requirements as a flow regardless of existing automation. Let's say a consultant developer created you a trigger similar to mine above and now you need to make an additional change to the record. If you do this with a flow, make sure to test this thoroughly.  Out of curiosity, I tested combining my trigger that sets the opportunity name with a flow that tweaks the amount by a very small amount.

The limit stats for this were frankly terrifying:

Number of SOQL queries: 7 out of 100
Number of query rows: 2219 out of 50000
Maximum CPU time: 8404 out of 10000 *****

So the CPU time has increased five fold by adding in a flow that by itself consumes almost nothing!