Tuesday, December 04, 2018

Test.loadData on Pricebook, Product, PriceBookEntry and RecordType


I remember when I was writing test class; Pricebook and the gang (Pricebookentry, Product) always giving me headache before I able to grab the concept. I also try to experiment on how to populate test data using Test.loadData on those object and quickly gave up as I was chasing for the dateline.

When I have a dateline, I always finding myself to choose familiar road although it can be slow because I know I can choose either to walk or run instead trying other new road which I unsure whether I able to walk or run or maybe eaten by monster.*who knows*

 I was once asked question here , the answer give me a hope that Test.loadData seem possible to be try on those object. After doing some experiments, here my conclusion :

1. Define relationship with Id and the Id is not necessary all integer. 

If you notice in my previous post, you will notice that I use all number to represent Id BUT it does not mean that we need to use all number. For Id, we can use alphanumeric so for example below. Or we can define Account Id like this; Account1, Account2 whatever we think it convenient and readable as we might have lot of records to populate.

Example 1


2. Use correct API Name regardless what.

Yes, that is the mantra.*what on earth you were thinking if you are not using API NAME* However common mistake on relationship –use correct API Name that define the Id. For standard object for example Opportunity, it use standard name such AccountId store Account Id. In case we create custom object, please check what is API Name that define the relationship. Some of common mistakes is using AccountId while actually it is Account__c .

3. RecordType IS NOT INSERTABLE from Test.loadData, therefore USE REAL ID

There is no such thing like creating csv file with all RecordType record *at least for this time being* and call Test.loadData. It will throw compilation error :

Method does not exist or incorrect signature: void loadData(Schema.SObjectField, String) from the type System.Test

If our object requires Record Type, then use real RecordTypeId . Example above in Example 1 shows how I populate record type id in csv file. The disadvantage of this : Id might be different so you need to update your csv file but the advantage is it easy to update and I found that unlikely to have different recordtype id in sandbox.*I leave this for you to decide*

4. Standard PriceBook IS NOT INSERTABLE , therefore USE REAL ID

*Do not even dream to insert Standard PriceBook * .Please remember in test class we need to use Test.getStandardPriceId() .HOWEVER we still able to populate PriceBookEntry using Test.loadData by using real Standard Price Book Id as example below. Note we still can populate custom PriceBook using Test.loadData .

5. First is first , last is last.Follow the sequence

Please ensure the sequence of insertion.This rule applies regardless using csv or code. PriceBookEntry cannot be inserted without PriceBook and Product , Opportunity cannot be inserted without Account.Follow the rules,  please put proper comment so other developer will not move the code. Put helpful Description in Static Resource and what it takes to help future generation.

I hope you find this article helpful. Let me know any method that you find useful.

Thanks.

Friday, November 09, 2018

Experiment : Test.loadData . Upload test data with static resources

Test.loadData is a method that enables us to create test data by using Static Resource. This was released quite sometimes but I haven't use it intensively. I experienced using it to load data in bulk because I wanted to test governor limit.

Recently we plan to use this on our project instead of using test data factory.Pushing test data using csv files are much easier compare to code insertion and define relationship.

I did some simple proof of concept using code below :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
@isTest
private class DataUtil {
    static testmethod void testLoadData() {
        // Load the test accounts from the static resource
        List<sObject> ls = Test.loadData(Account.sObjectType, 'AccountData');
        // Verify that all 3 test accounts were created
        System.assert(ls.size() == 3);

        // Get first test account
        Account a1 = (Account)ls[0];
        String acctName = a1.Name;
        System.debug(acctName);
        
        a1.Name ='New Name';
        
        update a1;
        
        Account a2 =[Select Id,Name from Account Where Name ='New Name'];
        
  //check if account get updated
        system.debug('After update a2 '+ a2.Name);
        
        Contact contact =new Contact (FirstName='TestContact',LastName='Lasting',AccountId=a2.Id);
        insert contact;
        
        Contact contact1=[select FirstName from Contact Where AccountId=:a2.Id];
        
        //yes , able to create contact on the account
        system.debug('After update contact1'+ contact1.FirstName);
        
        delete a2;
        
        List<Account> listAccount=[select Id from Account where Name='New Name'];
        //yes, able to delete
        system.assertEquals(0,listAccount.size());

    }
}

1. Can I update the record that I have loaded?
    Yes
2. Can I create child to the record that I have loaded?
     Yes
3. Can I delete the record that I have loaded?
    Yes.
4. Will insert trigger cover using Test.loadData?
    Yes, but validation rules is still applied.
5. Can I define relationship for example between Account, Opportunity and Contact?
     Yes you can.You can define it with mockup id in the column like below

When defining relationship, make sure that you run loadData method in proper order : master first then follow by child.In the example below, load Account first then Opportunity then Contact.

1
2
3
4
        // Load the test data  from the static resource in proper order
        List<sObject> lstAccount = Test.loadData(Account.sObjectType, 'AccountData');
  List<SObject> lstOpportunity = Test.loadData(Opportunity.sObjectType,'OpportunityData');
        List<SObject> lstContact = Test.loadData(Contact.sObjectType,'ContactData');

Some useful links to check out  :


Hope this help.

Tuesday, October 30, 2018

Process Builder is not fired when field update is called from Approval Process

Scenario
In Final Approvals section ; in Approval Process we have field update to update Status field. In Process Builder , we have some action that need to be done when Status field is updated in Approval Process.However this process builder is not fired.

Solution
To handle this, in Field Update in Approval Process , check Re-evaluated Workflow Rules after Field Change as picture below.



What happen if field updated from Approval Process.
  1. Workflow - does not fires untill Re-evaluate workflow checkbox is ticked on your field update
  2. Process Builder - does not fires untill Re-evaluate workflow checkbox is ticked on your field update
  3. Trigger - will fire if conditions are matched

This is explained in article here .

Field Updates That Re-evaluate Workflow Rules

If Re-evaluate Workflow Rules After Field Change is enabled for a field update action, Salesforce re-evaluates all workflow rules on the object if the field update results in a change to the value of the field.
  • If the field update changes the field’s value, all workflow rules on the associated object are re-evaluated. Any workflow rules whose criteria are met as a result of the field update will be triggered.
  • If any of the triggered workflow rules result in another field update that’s also enabled for workflow rule re-evaluation, a domino effect occurs, and more workflow rules can be re-evaluated as a result of the newly-triggered field update. This cascade of workflow rule re-evaluation and triggering can happen up to five times after the initial field update that started it.
  • Make sure that your workflow rules aren’t set up to create recursive loops. For example, if a field update for Rule1 triggers Rule2, and a field update for Rule2 triggers Rule1, the recursive triggers may cause your organization to exceed its limit for workflow time triggers per hour.
  • In a batch update, workflow is only retriggered on the entities where there is a change.
  • Only workflow rules on the same object as the initial field update will be re-evaluated and triggered.
  • Only workflow rules that didn’t fire before will be retriggered.
  • Cross-object workflow rules aren’t candidates for re-evaluation.
  • Cross-object field updates that cause a field value to change don’t trigger workflow rule re-evaluation on the associated object.
  • An approval process can specify a field update action that reevaluates workflow rules for the updated object. If, however, the re-evaluated workflow rules include a cross-object field update, those cross-object field updates are ignored.
  • Time-dependent actions aren't executed for a reevaluated workflow rule in the following situations:
    • The reevaluated workflow rule’s immediate actions cause the record to no longer meet the workflow rule criteria.
    • An Apex after trigger that is executed as a result of a workflow or approvals action causes the record to no longer meet the workflow rule criteria.

Discussion about this can be found here .

Thursday, October 18, 2018

What happen on Content Document Link if we upload Salesforce Files from record?

This entry specifically to know more about ContentDocumentLink which are not represented in diagram below.I happen to know more about this object after I experimenting on content's trigger(in future post).

To understand how the content relationship works please refer to diagram below .
 content objects diagram version 44.
refer to Salesforce for latest diagram

The highlight of this article is to identify ContentDocumentLink object that not appear in the diagram above.

ContentDocumentLink is Represents the link between a Salesforce CRM Content document or Salesforce file and where it's shared.
Field LabelAPI Field NameTypeLengthPrecisionScale
ContentDocumentLink IDIdid15
DocumentContentDocumentIdreference15
Is DeletedIsDeletedboolean
Linked EntityLinkedEntityIdreference15
Share TypeShareTypepicklist40
System ModstampSystemModstampdatetime
VisibilityVisibilitypicklist40
Experiment : Upload a file from Opportunity record.Once upload, click on the file and copy the id in URL. The id is represent contentdocumentid.Open Developer Console --> Execute Anonymous. Paste this code with contentdocumentid that you got.


1
2
3
system.debug('@@@ ContentVersion :  '+new List<ContentVersion> ([select Id ,ContentDocumentId from ContentVersion where ContentDocumentId ='06990000005Eor5']).size());
system.debug('@@@ ContentDocument :  '+new List<ContentDocument> ([select Id  from ContentDocument where Id ='06990000005Eor5']).size());
system.debug('@@@ ContentDocumentLink :  '+new List<ContentDocumentLink> ([select Id ,ContentDocumentId from ContentDocumentLink where ContentDocumentId ='06990000005Eor5']).size());

The result is like below, notice that we have two contentdocumentlink records.

To check what actually record being inserted in ContentDocumentLink, please run SOQL below:
select Id, ContentDocumentId, LinkedEntityId, IsDeleted, LinkedEntity.Type from ContentDocumentLink where ContentDocumentId ='06990000005Eor5' 
Result - LinkedEntity.Type are User (who upload the File) and Opportunity (the record where the file is uploaded)

Hope this will give you clear picture on what ContentDocumentLink object is doing.I am doing testing and find something interesting on content object which I will share with you later.

Happy coding

Tuesday, September 18, 2018

Dynamic Routing Approval

Use Case 

When Sales Rep submits Opportunity it requires different approver for different approval stage for different region.

Solution 

Create object to store approver details such for stage and region.Create Opportunity field to store approver for that particular Opportunity, these fields can be referred in Approval process. Using trigger query the approvers record and populate it in Opportunity based on specific criteria.

Meanwhile you can take a look in Appexchange, there an apps named Dynamic Approval Routing.

It is unmanaged package so we can view and modify the code to suit our need.The apps actually is a sample on how we can define approver for specific object.Its rule defined in the code and you might have different approver criteria and different object.The concept is

  1. to have one object to store approver details and
  2. create fields on object that going to be approved.
  3. create trigger on object that going to be approved to populate approver fields.
Design structure


Conclusion

Based on my experience especially for Opportunity object, I am still looking for alternative solution beside just create custom fields on Opportunity object because our Opportunity always hit custom fields limit.We do some clean up and at the end we have no more fields to create.

I am thinking maybe this can be solved using Queue to store Approver but it has limitation where it's not available for email response.There an idea for this here which still under threshold.

Do you face any issue related to this before? How you solve it?

Refer : Dynamic Approval Routing


Tuesday, September 04, 2018

Dynamic SOQL with SubQuery using SObject class

Requirement to have dynamic query that enable to retrieve the child of the parent objects.For example query the Opportunities field while querying Account instead of run separate query for each object.

For this post, I hard code object name and field name which actually can be done dynamically.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
String childF   = 'Name'; 
String childO_1 = 'Opportunity'; 
String childF_1   = 'Name';      

 if(childO  == 'Contact'){ 
     childO = 'Contacts'; 
 } 
 if(childO_1  == 'Opportunity')
  { childO_1 = 'Opportunities';
 } 

// create dynamic SOQL query string 

String query = 'SELECT id, (select ' +childF+ ' from ' +childO+') ,(select '+childF_1 + ' from '+childO_1+') FROM ' +parentO;

// query and store  results in variable p 

SObject[] p = Database.query(query);

for(Integer i = 0; i < p.size(); i++){ 

    system.debug('Parent result from SOQL query: ' +p[i]);

  //sample output ::: Parent result from SOQL query: Account:{Id=0010Q000002RtLCQA0, RecordTypeId=012a00000018HPEAA2, CurrencyIsoCode=GBP}    
 
 //this can  be handled dynamically   
  if(p.get(i).getSObjects(childO) <> null){       

   system.debug('Child result(s) from SOQL query: ' +p.get(i).getSObjects(childO));      
   //sample output :: Child result(s) from SOQL query: (Contact:{AccountId=0010Q000002RtLCQA0, Id=0030Q0000022un6QAA, Name=Clara James, RecordTypeId=0121p0000006x5bAAA, CurrencyIsoCode=GBP})     }  
   }
   if(p.get(i).getSObjects(childO_1) <> null){      
   system.debug('Child result(s) from SOQL query: ' +p.get(i).getSObjects(childO_1));      

   //sample output :: Child result(s) from SOQL query: (Opportunity:{AccountId=0010Q000002RtLCQA0, Id=0060Q000001s6nMQAQ, Name=Test Opportunity, RecordTypeId=012a00000018HPLAA2, CurrencyIsoCode=GBP}, Opportunity:{AccountId=0010Q000002RtLCQA0, Id=0060Q000001rpYaQAI, Name=Blue Hippo, RecordTypeId=012a00000018HPLAA2, CurrencyIsoCode=GBP})    

 }   

 }

In order to retrieve the child use getSObjects() .

The code above is adapted from this forum.


Tuesday, August 21, 2018

Paused and Waiting Interviews : Salesforce warns about inactive user


We receive an email from Salesforce saying
Every waiting flow interview has an associated user — the person who caused the interview to start waiting. If that user is inactive, the interviews can't resume.John Doe (005xxxxxxxxxxxx) is inactive, so please delete the following flow interviews.
Without thinking too much I went straight away to the Process Builder to check if it uses hardcode Id referring to the inactive user and I found none. The process builder is fine.

The email was sent is to inform us that there some process builder on the waiting list and not able to get fired because it was created by an inactive user.

The user left the company for quite some time, how is possible for him to fire the process builder?

What is Paused and Waiting Interview? 
Paused and Waiting interview only applies on schedule action process builder or time-dependent action. You can check Paused and Waiting Interview at  Setup | Create | Workflows & Approvals | Flows and look at Paused and Waiting Interview section.



Why I receive the email above?
Because the process builder has Scheduled action to send an email alert on 15 days before Close Date. Let's say the Close Date is in next year and the process builder is fired this year. 

The action to send email will be listed in Paused and Waiting Interview. However, Salesforce will remove this interview from waiting list after it tried to execute once. I noticed that because when I clicked the link that provided in the email after investigation and the link is no longer available.
Data Not AvailableThe data you were trying to access could not be found. It may be due to another user deleting the data or a system error. If you know the data is not deleted but cannot access it, please look at our support page.
The email  just to warn us in case we got many more interview in pending that owned by the user.

What will happen if we deploy a new version of Process Builder while some of the previous version in pending list?
Any deactivated version Process Builder will continue as usual and Salesforce will not allow us to delete this version. Refer to link here. If we think the previous version should not run then we should go and delete the waiting list.


Some related links that you might find useful.

Tuesday, August 14, 2018

Showing and hiding the tab in Salesforce Lightning Design System in Visualforce page using basic javascript

I am not yet familiar with Salesforce Lightning Design System (SLDS) however I need to start it somewhere.There some requirement come in and it does not have specification on what the UI look likes so I thought why not give SLDS a try.

Before SLDS comes into picture, I experienced using Bootstrap and JQuery. It's simple and easy to use however because it is external javascript library it need to have extra precautions.Sometimes we got issue when the css is conflict.

Still after SLDS , developer need to code javascript in order to enhance user experience for example changing the tab, make the inactive button visible after click as SLDS does not have code for that.It only provide you the CSS how active and disable button looks like.I notice in some demo they use React JS or JQuery.

I love it because it's neat and elegant but I found it hard to customize the theme if our organization has different color scheme from Salesforce. It can be customized using Design Token using Sass, then generate our own CSS zip file. So we can't use tag   for customize CSS. Yet, this  is another new thing to learn.

I am very reluctant to use JQuery in this because I want to avoid third party libraries.While you go through design,  you might notice, all the code that control look and feel in SLDC is defined in class.Therefore we can use Java Script to manipulate CSS to change look and feel.

Here is sample of the tab that I designed using SLDS.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
<div class="slds-tabs_card slds-tabs_default">
  <ul class="slds-tabs_default__nav" role="tablist">
    <li id="first_tab_header" class="slds-tabs_default__item slds-is-active" title="First Stage Question" role="presentation">
      <a class="slds-tabs_default__link" href="javascript:showTab('first-tab',this);" role="tab" tabindex="-1" aria-selected="false" aria-controls="second_tab" id="second_tab__item">First Stage Question</a>
    </li>
       <li id="first_tab_header" class="slds-tabs_default__item" title="Second Stage Question" role="presentation">
      <a class="slds-tabs_default__link" href="javascript:showTab('second_tab',this);" role="tab" tabindex="0" aria-selected="true" aria-controls="first_tab" id="first_tab__item">Second Stage Question</a>
    </li>
  </ul>
    <!--first tab start -->

    <div id="first_tab" class="slds-tabs_default__content slds-hide" role="tabpanel" aria-labelledby="first_tab__item">
     <!--content on first tab whatever --->

    </div> 
     <!--first tab end -->
  <!--second tab start -->
   <div id="second_tab" class="slds-tabs_default__content slds-show" role="tabpanel" aria-labelledby="second_tab__item">
     <!--content on second tab whatever --->
   </div>
<!--second tab end -->
</div>

The javascript code as below, it use classList remove and add .


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
function showTab(tabName, element){
    console.log('tabName '+ tabName);
    console.log('Element '+ element);
    if(tabName=='first_tab') {
     document.getElementById('second_tab_header').classList.remove('slds-is-active');     
    document.getElementById('first_tab_header').classList.add('slds-is-active');
        
    document.getElementById('second_tab').classList.remove('slds-show');
    document.getElementById('second_tab').classList.add('slds-hide');
    
    document.getElementById('first_tab').classList.remove('slds-hide');
    document.getElementById('first_tab').classList.add('slds-show');
    
    }
    else{
    document.getElementById('first_tab_header').classList.remove('slds-is-active');     
    document.getElementById('second_tab_header').classList.add('slds-is-active');  
        
    document.getElementById('first_tab').classList.remove('slds-show');
    document.getElementById('first_tab').classList.add('slds-hide');
    document.getElementById('second_tab').classList.remove('slds-hide');
    document.getElementById('second_tab').classList.add('slds-show');
    
    }

}

The javascript code is to change the CSS value that define in class .This is very basic article but I hope it will be helpful for you.Have you tried SLDS in your organization? How do you find it?

Have a nice day.

Reference :
Lightning Design System
Using SLDS in Visualforce Page
How to change customize theme, background color in Visualforce that using Design system?

Tuesday, July 31, 2018

Trapped in Process fails with error 'The flow failed to access the value because it hasn't been set or assigned' WHILE IT IS NOT


If you have no idea about Known Issue : Process fails with error 'The flow failed to access the value because it hasn't been set or assigned' , it's worth to check it first then you might want to read this entry.
Issue : The user reports that when using Mass Edit to edit Opportunity it will prompt error

Can't Save Record

That's it. No sweet lovely sentence, no flowery and it's leave the mystery for us to dig out the root cause.Typical communication breakdown in love story.

Luckily we have email system whereby we got chance to get the exception email straight in our inbox.Here the error :


Error element myDecision (FlowDecision).
The flow failed to access the value for myVariable_current.Owner.UserRole.DeveloperName because it hasn't been set or assigned.

Just to clarify , yes we use process builder and we are aware of issue above whereby we need to put condition checking in proper order whenever our condition need to check value from other object.This is known issue described here.

A Process that makes a cross-object reference will fail when the foreign key (i.e. relationship field) the reference depends on is not populated.

Our process builder complied with the rules still it happening?

Why  error?

To test , I modified from condition based to formula well something like this.

AND(NOT(ISBLANK([Opportunity].Owner.UserRoleId  )) , OR (  [Opportunity].Owner.UserRole.DeveloperName ='Sales Manager' ,  [Opportunity].Owner.UserRole.DeveloperName ='APAC Manager'') ) 

and pray to God.

Unfortunately it's still happening and I am not able to get much information when Googling.
.
.
.

How the user input?
Using Mass Edit.

Is Process Builder does not support bulk?
Nope- process builder support bulkified .This is delivered in Winter 16. Plus the exception that we face nothing to do with limit.

What is MassEdit?
It is visualforce page that we installed long time ago.

Is it using customize controller?
No, it using standard controller.The save still same like standard, the only thing that different is the UI.No fancy code.
.
.
.
Click on Version Settings of visualforce page.
Version 15.
15...
Change to 42 and pray to God.

Success. No Error.

Yes, upgrade version can solve problem.It will be easier if the error message tell me something wrong with version.

Typical communication-breakdown =)

Thursday, July 05, 2018

Pricebook, Pricebook Entry and Product : Adding custom pricebook in Salesforce

Lately I worked on these object a lot and learned a lot of new things.

Some issues that we face around these object :
  • Some exceptions during data upload  such as :
    • FIELD_INTEGRITY_EXCEPTION .Message :field integrity exception: PricebookEntryId, unknown (versions 3.0 and higher must specify pricebook entry id, others must specify product id)
    • No Standard Price defined
  • Not able to retrieve standard price in test class


Understand the relationship of these object


Ignore Opportunity and Opportunity Line Item- these two object are using Price Book entry .Image credit to ForceTalk forum

PriceBook

  • Pricebook object store a list of products and services for sale.
  • A pricebook can be used in one or more different opportunities.
  • A pricebook has one or more different price entries.
  • Pricebook can be divided into type which are :
    •  Standard Price Book which is the master list of all the products and their default standard price.
    • Custom Price Book is separate list of product with custom price - called list price.


Product2

  • Product object is a catalogue of items or services for sales.
  • A product may contain one or more different sets of prices (PriceBookEntry).

PriceBookEntry

  • PricebookEntry object is a product entry in a pricebook.
  • This object allows products to be linked to standard price book or custom price book.
  • One price book entry is linked to only one product.
  • One price book entry can only appear in one pricebook.
  • One price book entry can be used in multiple line items. This also further creates flexibility for users to set their own prices.

Rules for Data Upload

  • PriceBookentry is junction object that connect Product and PriceBook so the order will be like this :
    • Insert PriceBook
    • Insert Product
    • Insert PriceBookEntry with PricebookId and Product Id

Scenario 1 : I am not able to insert PriceBookEntry for custom PriceBook.

We already have custom PriceBook and Product data inserted but we not able to insert PriceBookEntry to link these two objects

Reason : In order to insert PriceBookEntry for custom PriceBook , those Products must be added to Standard PriceBook first because Standard PriceBook is considered as master price. 

Refer knowledge articles here : 

Scenario 2 : Standard Price is not querable and insertable in test class

Well nothing I can do much because this how it works currently. But I can give some advice :

If the code contains SOQL on Pricebook make sure it get surrounded with try and catch. Add Test.isRunningTest() checking in the code and assign Standard Price Book.
If you have custom price you may want get an idea how to handle catch based on code below

 catch (Exception e){
   if(Test.isRunningTest()){
      List<Pricebook2>  listPB= [ select Id ,Name , IsStandard from Pricebook2 where Name ='Custom'  LIMIT 1];
      mapPriceBook.put('Standard',Test.getStandardPricebookId() );
      mapPriceBook.put('Custom',listPB[0].Id);
    
   }
 }

Also avoid retrieve and using PriceBook object but use Id.

Example of code that you want to avoid.Note that there is no way for you to get away with Test class exception because you only can use Test.getStandardPriceBookId() , not the whole object in test class.


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
List<Pricebook2> listPB = [select Id ,Name , IsStandard from Pricebook2 where IsStandard = True OR  Name ='Custom'  LIMIT 2];

static  Map<String,Pricebook2> mapPriceBook =new Map<String,Pricebook2> ();

for(Pricebook2 pb : listPB){

if(pb.IsStandard){
  mapPriceBook.put('Standard',pb); //this will not get over the test class
}
else if(pb.Name =='Custom'){
 mapPriceBook.put('Custom',pb);
}

One way to be safe is using Id.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
List<Pricebook2> listPB = [select Id ,Name , IsStandard from Pricebook2 where IsStandard = True OR  Name ='Custom'  LIMIT 2];

static  Map<String,Id> mapPriceBook =new Map<String,Id> ();
             
             for(Pricebook2 pb : listPB){
             
               if(pb.IsStandard){
                  mapPriceBook.put('Standard',pb.Id);
               }
               else if(pb.Name =='Custom'){
                 mapPriceBook.put('Custom',pb.Id);
               }

I think that's all I can think today. I believe you also got some of tips and trick to get over exception related to these objects. Please let me know how you do that?

References


Monday, April 02, 2018

Add Fault Connector in Salesforce Flow Autolaunch


Fault Connector is very useful because now we able to handle the error. We can trace who perform the transaction and what record that cause the error.

The connector is indicated with FAULT like image below.


In some occasion you might face into issue that Fault Connector is not working like below :




Here is the hack. We need to add another element after it because for unknown reason Fault Connector will not appear in the last element. So we need to create another element temporarily like below.

Tips : Use Copy and Paste button to copy from existing element so you don't have create dummy element from start.


Now we can add Fault Connector to the element that we want to.As you can see in the image below, when we drag the connector 'Fault' label appear. Now we can delete the dummy element.

That's it. Now it should works.

More about Flow can be refer to here .

Have a nice day.