+ Start a Discussion
Lisa FrenchLisa French 
I had some help writing a trigger to pull information from a description field into account lookup fields so that we can click on the name and have quick access to the associated account record.  The problem is that now when we try to change that field, it will not let us.  We can change it, but as soon as we hit save, it goes back to the auto-populated data pulled in from the trigger.  We need to be able to change that and I am not a Salesforce Developer, I am an admin and have been puzzling over this way too long.  Any help would be greatly appreciated.  Please see the code below:

Trigger OpportunityTrigger on Opportunity (after insert, after update) {
    public List<String> contactTypeList = New List<String>{'General Contractor','Architect','Developer','Owner','Consultant'}; 
    LargoDataTest__c ltd = LargoDataTest__c.getOrgDefaults();
    if(trigger.isAfter && APEXUtil.IsNewTrigger==false && !ltd.StopTrigger__c){
            List<Opportunity> AllOppList = New List<Opportunity>();             
            List<Opportunity> OpptoUpdateList = New List<Opportunity>(); 
            List<Account> AcctoInsertList = New List<Account>(); 
            List<String> AccNames = New List<String>();
            MAP<String,String> contactTypeMAP = New MAP<String,String>(); 
            MAP<String,Account> accMAP = New MAP<String,Account>();
            MAP<Id,MAP<String,String>> OpportunityMAP = New MAP<Id,MAP<String,String>>();
            for(Account acc: [Select Name From Account]){accMAP.put(acc.Name, acc);} 
            for(Opportunity opp : trigger.new){AllOppList.add(New Opportunity(Id=opp.ID, Description = opp.Description));} 
            for(Opportunity opp : AllOppList){ 
                if(opp.Description!=null?opp.Description.contains('Contact Information')?true:false:false){               
                    contactTypeMAP = getContactType(opp.Description, contactTypeList);
                    for(String AccName: contactTypeMAP.values()){if(!AccNames.Contains(AccName)?(AccName!=null?true:false):false){AccNames.add(AccName);}}                    
                    for(String AccName: AccNames){if(!accMAP.Containskey(AccName)){AcctoInsertList.add(New Account(Name=AccName));}}                   
                Database.SaveResult[] rsResult = Database.Insert(AcctoInsertList,false);
                for(Database.SaveResult database: rsResult){if(!database.isSuccess()){for(Database.Error err : database.getErrors()) {System.debug('Insert Error-->'+err.getMessage());}}}
                for(Account acc: AcctoInsertList){if(acc.Id != null) accMAP.put(acc.Name, acc);}
            for(Opportunity oppT : AllOppList){
                for(Id oppId : OpportunityMAP.keySet()){
                    if(oppT.Id == oppId){
                        oppT.General_Contractor__c=OpportunityMAP.get(oppId).Containskey('General Contractor')?(accMAP.containsKey(OpportunityMAP.get(oppId).get('General Contractor'))?accMAP.get(OpportunityMAP.get(oppId).get('General Contractor')).Id:null):null;
                APEXUtil.IsNewTrigger = true;                
                Database.SaveResult[] rsResult = Database.update(OpptoUpdateList,false);
                for(Database.SaveResult database: rsResult){if(!database.isSuccess()){for(Database.Error err : database.getErrors()) {System.debug('Update Error-->'+err.getMessage());}}}          
    public MAP<String,String> getContactType (String Description, List<String> contactTypeListAux){
        MAP<String,String> contactTypeMAP = New MAP<String,String>();
        Integer indexCTAux0 = Description.indexOf('Contact Information'), indexCTAux1 = -1,indexCTAux2 = -1;     
        for(String ct: contactTypeListAux){            
                indexCTAux1 = Description.indexOf(ct+':',indexCTAux0)!=-1?Description.indexOf(ct+':',indexCTAux0):Description.indexOf(ct+',',indexCTAux0)!=-1?Description.indexOf(ct+',',indexCTAux0):-1;
                System.debug('ct1-->indexCTAux1:' + ct+'-->'+indexCTAux1);
                if(indexCTAux1!=-1?(Description.charAt(indexCTAux1-1)==10 || Description.charAt(indexCTAux1-1)==32?true:false):false){                        
                        indexCTAux1 = Description.indexOf(ct+':',indexCTAux2+1)!=-1?Description.indexOf(ct+':',indexCTAux2+1):Description.indexOf(ct+',',indexCTAux2+1)!=-1?Description.indexOf(ct+',',indexCTAux2+1):-1;
                        if(indexCTAux1!=-1?(Description.charAt(indexCTAux1-1)==10 || Description.charAt(indexCTAux1-1)==32?true:false):false){
        return contactTypeMAP;
    public String getAccount (String Description, Integer indexCT){
        Integer StartNA = Description.indexOfChar(10,indexCT); 
        Integer EndNA = StartNA!=-1?Description.indexOfChar(10,StartNA+1):-1;
        return StartNA!=-1 && StartNA!=-1?Description.substring(StartNA+1, EndNA-1):Null;
Best Answer chosen by Lisa French
AbhishekAbhishek (Salesforce Developers) 

Use the code snippet as mentioned suggested in the above blog.

For further reference check this too (https://trailblazers.salesforce.com/answers?id=9063A000000pOAvQAM).
Evan KimmeyEvan Kimmey 
I am trying to populate an account field that is Free Text (Long) with a multi-select picklist field on the opportunity object. Workflow builder doesn't allow for a multi-select picklist cannot be used as the operator. In process builder I was running into the issue where I could not select the desired multi-select opp field. Is this because of the field type being multi-select or is there something I am missing?
Best Answer chosen by Evan Kimmey
AbhishekAbhishek (Salesforce Developers) 
I don't think we can do that unless you override the Standard Page Layout with a Custom Visualforce if you need this to happen real-time! Otherwise, it would be like this - 

User selects the Options from the Multi-select Picklist
Hit the Save button
A Workflow Rule would execute and as a result of which a Field Update fires.This fills the Long Text Area with all the "selected" Competitors line by line.
But the caveat is that, you or the User has to hit the Save button and then edit again to start filling the same.

Another option would be do this using a Flow. Where - 

The first Screen would show options[Multi-select Values aka the Competitors]
And then Loop iterates over the collection and show Screens to enter values for each of them
Finally collects the required information in a Text based Variable
Updates the Record
Yes, it sounds a little to overwhelming but I feel it should be doable.

And I also strongly feel that these should have been separate entities or objects instead of the Multi-Select Picklist and Long Text Area!

You can think of it like this - There will be a Custom Object called Competitor which would store the Names and Details of all know Competitors. You can then create another Custom Object called say "OpportunityCompetitor" which will be a junction object between Competitor and Opportunity(I am using Opportunity as an example). This could house the Price and other relevant fields too!

Yes it looks a little too broad and may it's late for you but still worth from a "long run" standpoint. As compared to the native options, there are ways to customize and the data becomes reportable which is what we need at the end of the day!

Let me know if it helps you and close your query by marking it as solved so that it can help others in the future.

Jauhien LeaniukJauhien Leaniuk 
Only name column in my table shown when I start my app.
When I type something in search box or all fields of the table appears, but at the start of the component, only name filed showed.

Here is my
<aura:component controller="SortableAccountTableController"
                description="Account table with column sorting example"
    <aura:handler name="init" value="{!this}" action="{!c.onInit}"/>
   <!--aura attributes--> 
    <aura:attribute name="contactColumns" type="List"/>
    <aura:attribute name="contactData" type="Object"/>
    <aura:attribute name="sortBy" type="String"/>
    <aura:attribute name="sortDirection" type="String"/>
    <aura:attribute name="filteredData" type="Map"/>
    <!--Page header-->
    <div class="slds-page-header" role="banner">
        <span class="slds-text-heading_medium">Contacts List</span>
        <span class="slds-page-header__title">10 per Page</span>
    <!-- New button added -->
	<lightning:button label="New" onclick="{!c.newContact}" />
    <lightning:input onchange="{!c.searchTable}" type="search" label="Searh" variant="label-hidden" placeholder="Enter search term" aura:id="SearchBox"/>
    <!--Lightning data table markup-->
    <lightning:datatable aura:id="accountTable"


    onInit : function(component,event,helper){
        // Setting column information.To make a column sortable,set sortable as true on component load
            { label : 'Name', fieldName : 'Name', type : 'Name', sortable : true },
            { label : 'Phone', fieldName : 'Phone', type : 'Phone', sortable : true },
            { label : 'Contact Level', fieldName : 'Contact_Level__c', type : 'Picklist', sortable : true },
            { label : 'Account', fieldName : 'Account', type : 'Lookup', sortable : true },
            { label : 'Owner', fieldName : 'OwnerId', type : 'lookup', sortable : true },
            { label : 'Created By', fieldName : 'CreatedById', type : 'Lookup(User)', sortable : true },
            { label : 'Created Date', fieldName : 'CreatedDate', type : 'Date/Time', sortable : true },
        var action = component.get("c.fetchData");
        action.setCallback(this, function(response){
            var state = response.getState();
            if (state === "SUCCESS") {
                component.set("v.contactData", response.getReturnValue());
                component.set("v.filteredData", response.getReturnValue());
        // call helper function to fetch account data from apex
    //Method gets called by onsort action,
    handleSort : function(component,event,helper){
        //Returns the field which has to be sorted
        var sortBy = event.getParam("fieldName");
        //returns the direction of sorting like asc or desc
        var sortDirection = event.getParam("sortDirection");
        //Set the sortBy and SortDirection attributessadsad
        // call sortData helper function
   	searchTable : function(cmp,event,helper) {
        var allRecords = cmp.get("v.contactData");
        var searchFilter = event.getSource().get("v.value").toUpperCase();
        var tempArray = [];
        var i;

        for(i=0; i < allRecords.length; i++){
            if((allRecords[i].Name && allRecords[i].Name.toUpperCase().indexOf(searchFilter) != -1))
    // Function used to create a new Contact
    newContact: function(component, event, helper) {
        // Global event force:createRecord is used
        var createContact = $A.get("e.force:createRecord");
        // Parameters like apiName and defaultValues are set
            "entityApiName": "Contact",
            "defaultFieldValues": {
                "AccountId": component.get("v.recordId")
        // Event fired and new contact dialog open

    getAccountData : function(component){
        //Load the Account data from apex
        var action = component.get("c.getAccounts");
        var toastReference = $A.get("e.force:showToast");
            var state = response.getState();
            if(state == "SUCCESS"){
                var accountWrapper = response.getReturnValue();
                    //Setting data to be displayed in table
                        "type" : "Success",
                        "title" : "Success",
                        "message" : accountWrapper.message,
                        "mode" : "dismissible"
                } // handel server side erroes, display error msg from response 
                        "type" : "Error",
                        "title" : "Error",
                        "message" : accountWrapper.message,
                        "mode" : "sticky"
            } // handel callback error 
                    "type" : "Error",
                    "title" : "Error",
                    "message" : 'An error occurred during initialization '+state,
                    "mode" : "sticky"
    sortData : function(component,fieldName,sortDirection){
        var data = component.get("v.filteredData");
        //function to return the value stored in the field
        var key = function(a) { return a[fieldName]; }
        var reverse = sortDirection == 'asc' ? 1: -1;
        // to handel number/currency type fields 
        if(fieldName == 'NumberOfEmployees'){ 
                var a = key(a) ? key(a) : '';
                 var b = key(b) ? key(b) : '';
                return reverse * ((a>b) - (b>a));
        else{// to handel text type fields 
                var a = key(a) ? key(a).toLowerCase() : ''; //To handle null values , uppercase records during sorting
                var b = key(b) ? key(b).toLowerCase() : '';
                return reverse * ((a>b) - (b>a));
        //set sorted data to accountData attribute

Apex class
public class SortableAccountTableController {
   // wrapper class 
    public class AccountWrapper{
        public String message;
        public List<Contact> contactList;
        public Boolean success;
    //Return account records and message to be displayed in toast
    public static AccountWrapper getAccounts(){
        AccountWrapper accountWrapper = new AccountWrapper();
            accountWrapper.contactList = [SELECT ID, Name, Phone, Contact_Level__c, AccountId, OwnerId, CreatedById, CreatedDate
                                           FROM Contact
                                           LIMIT 50];
            accountWrapper.message = 'Account records are retrieved ';
            accountWrapper.success = true;
        catch(Exception e){
            accountWrapper.message = e.getMessage();
            accountWrapper.success = false;
        return accountWrapper;
    public static List<sObject> fetchData() {
        //Query and return list of Contacts
        List<SObject> objRecords = [SELECT Name from Contact LIMIT 20];
        return objRecords;

Best Answer chosen by Jauhien Leaniuk
Jauhien LeaniukJauhien Leaniuk
Ok I didnt pay attention to the last method. True version is this:
    public static List<sObject> fetchData() {
        //Query and return list of Contacts
        List<SObject> objRecords = [SELECT Name, Phone, Contact_Level__c, AccountId, OwnerId, CreatedById, CreatedDate from Contact LIMIT 20];
        return objRecords;

Create an Apex class that returns an array (or list) of strings: 
Create an Apex class that returns an array (or list) of formatted strings ('Test 0', 'Test 1', ...). The length of the array is determined by an integer parameter.The Apex class must be called 'StringArrayTest' and be in the public scope.
The Apex class must have a public static method called 'generateStringArray'.
The 'generateStringArray' method must return an array (or list) of strings. Each string must have a value in the format 'Test n' where n is the index of the current string in the array. The number of returned strings is specified by the integer parameter to the 'generateStringArray' method.

MyApexClass to above Challenge:

public class StringArrayTest {
    public static List<string> generateStringArray(Integer n)
        List<String> myArray = new List<String>();
        for(Integer i=0;i<n;i++)
        return myArray;

It's compile and execute as per requirement in Developer console. But Traihead showing the below error:

Challenge not yet complete... here's what's wrong: 
Executing the 'generateStringArray' method failed. Either the method does not exist, is not static, or does not return the proper number of strings.

Anyhelp would be greatly appreciated. Thanks.
Best Answer chosen by Sattibabu
Carolina Ruiz MedinaCarolina Ruiz Medina
Hi Sattibabu,
Please find below a little fix for your issue.

The problem was the space between Test and the number you had : Test0, Test1.... and what was necessary was Test 0, Test 1, ... 
public class StringArrayTest {
    public static List<String> generateStringArray(Integer n)
        List<String> myArray = new List<String>();
        for(Integer i=0;i<n;i++)
           myArray.add('Test '+i);
        return myArray;

Also I will say that if in the test we pass through a negative value it will create values in the array then maybe for real purposes you would like to validate that.

Hope it it helpful :)

SFDC Admin12SFDC Admin12 
Our app has the need of storing access_token and refresh_token in the DB, but as we compare the size to Salesforce's same tokens - it's much larger: access_token in MS 2150 chars vs access_token in SF 112 chars (similar ratio for refresh tokens). We are using the On-Behalf-Of flow : https://docs.microsoft.com/en-us/azure/active-directory/develop/v2-oauth2-on-behalf-of-flow My understanding is that OAuth2 spec wouldn't specify the size of the tokens, it's open to implementations. The question is if cookie based storage is not preferred, or for cases where custom apps would need to use DB access/refresh token serializations - isn't the length/size an issue or constrain? thanks
Best Answer chosen by SFDC Admin12
AbhishekAbhishek (Salesforce Developers) 
There are no known size limits for AAD tokens.

For further reference, you can check this too,


Let me know if it helps you and close your query by marking it as solved so that it can help others in the future.

Sascha DeinertSascha Deinert 

how can I add to my visualforce an inputfield with the possibility to change the formatting - text like bold or to change the color ....?  

Like this -->

Best Answer chosen by Sascha Deinert
ANUTEJANUTEJ (Salesforce Developers) 
Hi Sascha,

I see that it is similar to a rich text input field so you can try the below tag once:

<apex:inputtextarea label="RichText" richtext="true" value="{!body}" id="Body" cols="100" rows="15"/>

Link: https://salesforce.stackexchange.com/questions/135647/rich-text-area-field-in-visualforce-page

Let me know if it helps you and close your query by marking it as solved so that it can help others in the future.  

Developer BaseDeveloper Base 
Hello there!

I created two record types on the Account object. Record that were already existent remained with an unassigned record type Id. Seems like this is an expected behaviour, but I can't find an explanation in the documentation.

Can anyone help me with the research and explanation?

Thank you
Best Answer chosen by Developer Base
AnudeepAnudeep (Salesforce Developers) 
Yes, this is working as designed. We see this issue if the records were created before the two record types were created. There isn't any documentation that I can point you to but you can try setting an option to automatically insert your default record type when you create records.


I recommend taking a look at this post where this issue is already discussed 

Let me know if this helps, if it does, please close the query by marking it as solved. It may help others in the community. Thank You!
Shatrughna SalunkeShatrughna Salunke 

Is there any aletrnative way to achive the  below  sosl and soql error?

For the below code I am getting error

Error : Invalid IN fields group: PERSONEMAIL OR Custome_Email__C

   public static List<Account>  getData(String entityType)
       List<account> getDetails=new List<account>();
        List<List<Account>>  accList = [FIND : entityType IN PersonEmail Fields RETURNING Account(id,PersonEmail) limit 1];
        for(List<Account> accountList1 : accList){
            for(Account accountList2 : accountList1){
                if(accountList2.PersonEmail != null )
                    if((accountList2.PersonEmail).equalsIgnoreCase(entityType)  )
        return getDetails;

Also,getting error for this soql as well 
[Select id,PersonEmail  from Account where PersonEmail =:emailIds]

Error: we canot filter query for PersonEmail

Note: passing email id' from aura method and need to be filter record based on the email id's

Best Answer chosen by Shatrughna Salunke
AnudeepAnudeep (Salesforce Developers) 
Ran the SOQL query in my org where I have Person Account enabled and I am not seeing this issue

User-added image

Guess the error you are seeing with your SOSL is expected though because In SOSL you can't search by string value of a Lookup field.

For Example:

Record in object Contact:
Name: David Sidhu

Now there's a related object "LookUpToContact" looking up to parent object Contact.

Try to search "David Sidhu" using SOSL in LookUpToContact Query:

String searchName = 'FIND {David*} IN ALL FIELDS RETURNING LookUptoContact__c(Id, Contact__r.FirstName)';

Returns nothing, just an error (can be verified using workbench).

Patrick McClellanPatrick McClellan 
I'm having trouble validating challenge 2, with regard to Shinje Tashi's user settings.
Challenge Not yet complete... here's what's wrong: 
Shinje Tashi does not have the user settings he needs.

The instructions say: "This sales data quality specialist supports the entire company and needs to be assigned a role that can see and access all account, contact, and opportunity records for the entire company. Use the Standard User profile."

First off, it seems odd not to handle this through a separate profile, but I'm trying to follow the directions. So...

I created Shinje as an active user, User License: Salesforce, Profile: Standard User. There isn't a role specified for him, and the instructions suggest providing access to all acct, contact and oppty records through a role assignment, so I created a Sales Data Admin role, reporting to the CEO. That role should provide RECORD access. Additionally, for OBJECT access, I created a permission set granting full access (CRED, View All, Modify All) to Acct, Contact, and Oppty objects and assigned it to Shinje. I created a separate permission set granting access to the Language preference field on Acct, named that permission set "Bilingual Pilot" as instructed, and assigned it to Shinje.

I'm not sure what I'm missing...

Best Answer chosen by Patrick McClellan
Eric JJ van HorssenEric JJ van Horssen
Permission set not needed, The role you created will not be able to see teh records owned by the role above (CEO)

Set the Role simply to CEO
I dont know what's the error in this test class
In test Class Line no 4 List acc didn't return any value
Apex class
public with sharing class ShowOppurtunity {
    public static List<Opportunity> showoppurtunity(String AccId) {
        List<Opportunity> lstOpportunity = [SELECT Name,StageName,TrackingNumber__c,LeadSource,Description FROM Opportunity WHERE AccountId = :AccId ];
        return lstOpportunity;

Apex test Class

public with sharing class ShowOppurtunityTest {
    static testmethod void testData(){
        List<Account> acc = [SELECT Id FROM ACCOUNT LIMIT 1];
        if (acc != null) {
        List<Opportunity> opp =[SELECT Name,StageName,TrackingNumber__c,LeadSource,Description FROM Opportunity WHERE AccountId = :acc[0].Id];
        List<Opportunity> opt = ShowOppurtunity.showoppurtunity(String.valueOf(acc[0].Id));
Best Answer chosen by Satheesh1105
You need to create test data in your test class and only then your query will return the data. Refer this URl for details: