I am doing a fairly simple query for contact records using dynamic soql using the following method:
public PageReference contactSearch() {
contactResultSetSize = 0;
if(!String.isEmpty(firstname) || !String.isEmpty(lastname) || !String.isEmpty(company)) {
string soql = 'Select firstname, lastname, account.Name, account.BillingStreet, account.BillingCity, account.BillingState, account.BillingPostalCode From Contact';
String whereClause = '';
if(!String.isEmpty(firstname)) {
whereClause = ' Where firstname like \'%' + firstname + '%\'';
}
if(!String.isEmpty(lastname)) {
if(!String.isEmpty(firstname)) {
whereClause += ' AND lastname like \'%' + lastname + '%\'';
}
else {
whereClause = ' Where lastname like \'%' + lastname + '%\'';
}
}
if(!String.isEmpty(company)) {
if(!String.isEmpty(firstname) || !String.isEmpty(lastname)) {
whereClause += ' AND account.Name like \'%' + company + '%\'';
}
else {
whereClause = ' Where account.Name like \'%' + company + '%\'';
}
}
soql = soql + whereClause;
List<Contact> searchResults = Database.query(soql);
contactResultSetSize = searchResults.size();
if(contactLinesForPage == null) {
contactLinesForPage = new List<ContactWrapper>();
}
for(Contact c : searchResults) {
contactLinesForPage.add(new ContactWrapper(contactLinesForPage.size(), c, ''));
}
}
return null;
}
I am using a wrapper class and contactLinesForPage is a list of my wrapper object:
public List<ContactWrapper> contactLinesForPage {get; set;}
As a user does multiple searches, I don't want to re-add records to the searchResults list. How can I check if a record already exists in my object so I don't have duplicate records returned in the search?
Thanks for any help.
Or you could use a map. Add the ContactWrapper objects to the map. The key to the map is an id. If they add a duplicate contact it will simply overwrite the one already there. Your code would simply be
aMap.put(cw.id, cw); // one line eliminates duplicates.
When you want the list of ContactWrappers, simply return aMap.values();
If you want to abstract the behavior of maintaining the collection of Contacts, create a ContactCollection class and hide the implementation in there. This would provide something more reusable as well as a good pattern for similar situations.
Just add a check if contactLinesForPage allready contains this contact. Something like this:
for(Contact c : searchResults) {
Boolean toInsert = true;
for(ContactWrapper cw : contactLinesForPage){
if(cw.contact.Id == c.Id){
toInsert=false;
}
}
if(toInsert){
contactLinesForPage.add(new ContactWrapper(contactLinesForPage.size(), c, ''));
}
}
Related
I'm Working through this challenge and appear to have run into an issue i have not found a solution to (nor does it appear that anyone has posted a question on this specifically.)
looking at the log is pretty painful as any System.debug() lines appear to be dropped due to the size of the log being >18MB but it appears there are 40000 rows to be added. Having caved and looked at some of the other code dumps out there i dont see that anyone else looks to have had this issue or made any special arrangements with batching and as far as i can see i haven't done anything drastically different to them (but the subtlety is clearly more than enough to cause an issue).
So if anyone could have a look over this and give me a hint as to where/why i have gone wrong it would be a great help. I'm not looking for a link to a working code set to copy i'm hoping for some understanding as to where my approach is incorrect/causing issues
With my trigger
trigger MaintenanceRequest on Case ( after update) {
List<Case> closedCases = new List<Case>();
for (Case c : Trigger.New){
System.debug('Type :' + c.Type );
System.debug('Equals Repair: ' +c.Type.equalsIgnoreCase('Repair'));
System.debug('Equals Routine Maintenance: ' +c.Type.equalsIgnoreCase('Routine Maintenance'));
if (
(c.Type.equalsIgnoreCase('Repair') || c.Type.equalsIgnoreCase('Routine Maintenance'))
&& c.isClosed == true )
{
closedCases.add(c);
}
}
System.debug(closedCases.size() + ' cases were closed');
if(closedCases.size() > 0){
MaintenanceRequestHelper.createScheduledMaintainance(closedCases);
}
}
and helper
public with sharing class MaintenanceRequestHelper {
public MaintenanceRequestHelper() {
}
public static Map<Id,Integer> getMainainanceCycleForCases(){
List<AggregateResult> maintainTimes = [SELECT Maintenance_Request__c, MIN(Equipment__r.Maintenance_Cycle__c)cycle
FROM Equipment_Maintenance_Item__c
GROUP BY Maintenance_Request__c
];
System.debug(maintainTimes);
Map<Id,Integer> maintinTimeMap = new Map<Id,Integer>();
for( AggregateResult e : maintainTimes ){
maintinTimeMap.put((Id)e.get('Maintenance_Request__c'), ((Double)e.get('cycle')).intValue());
}
System.debug(maintinTimeMap);
return maintinTimeMap;
}
public static Map<Id,List<Id>> getMaintainanceItems(List<Case> closedCases){
// map maintainance_Request_ID, Equipment_Maintenance_Item__c
List<Equipment_Maintenance_Item__c> equipmentMaintainanceList = [SELECT Maintenance_Request__c,
Equipment__r.Id
FROM Equipment_Maintenance_Item__c
Where Maintenance_Request__c IN :closedCases];
Map<Id,List<Id>> maintainance_equipmentMap = new Map<Id, List<Id>>();
for (Equipment_Maintenance_Item__c e:equipmentMaintainanceList){
System.debug('Putting : '+ e.Maintenance_Request__c + ' : ' + e.Equipment__r.Id);
if(maintainance_equipmentMap.containsKey(e.Maintenance_Request__c)){
maintainance_equipmentMap.get(e.Maintenance_Request__c).add(e.Equipment__r.Id);
}else{
maintainance_equipmentMap.put(e.Maintenance_Request__c,new List<id>{e.Equipment__r.Id});
}
System.debug('Map now : '+ maintainance_equipmentMap);
}
System.debug('Returning: ' +maintainance_equipmentMap);
return maintainance_equipmentMap;
}
public static void createScheduledMaintainance(List<Case> closedCaseList) {
System.debug(closedCaseList.size() + ' Cases to create');
Map<Id,Integer> maintainTimeMap = getMainainanceCycleForCases();
Map<Id,List<Id>> maintainanceItems = getMaintainanceItems(closedCaseList); // map maintainance_Request_ID, Equipment_Maintenance_Item__c
List<Case> createNewCases = new List<Case>();
for(Case c : closedCaseList){
Case newCase = new Case();
newCase.Type = 'Routine Maintenance';
newCase.Status = 'New';
newCase.Vehicle__c = c.Vehicle__c;
newCase.Subject = c.Subject;
newCase.Date_Reported__c = Date.today();
newCase.Date_Due__c = Date.today() + maintainTimeMap.get(c.Id);
newCase.ProductId = c.ProductId;
createNewCases.add(newCase);
}
System.debug(createNewCases.size() +' to insert');
list<Equipment_Maintenance_Item__c> itemsListToinsert= new list<Equipment_Maintenance_Item__c>();
if(createNewCases.size()>0){
insert createNewCases;
for(Case ca : createNewCases){
for(Id key: maintainanceItems.keySet()){
List<Id> equipment = maintainanceItems.get(key);
for (Id e : equipment){
Equipment_Maintenance_Item__c newitem = new Equipment_Maintenance_Item__c();
newitem.Equipment__c=e;
newitem.Maintenance_Request__c= ca.Id;
itemsListToinsert.add(newitem);
}
}
}
}
System.debug('itemsListToinsert Size: ' +itemsListToinsert.size());
if (itemsListToinsert.size() >0){
insert itemsListToinsert; //<<<< ERROR TRIGGERS HERE and has ~40000 rows <<<<<
}
}
}
So, It seems i was over complicating the issue to an extent.
Having taken a break and walking through the problem again something just didnt sit right with this block of code.
if(createNewCases.size()>0){
insert createNewCases;
for(Case ca : createNewCases){
for(Id key: maintainanceItems.keySet()){
List<Id> equipment = maintainanceItems.get(key);
for (Id e : equipment){
Equipment_Maintenance_Item__c newitem = new Equipment_Maintenance_Item__c();
newitem.Equipment__c=e;
newitem.Maintenance_Request__c= ca.Id;
itemsListToinsert.add(newitem);
}
}
}
}
For -> For -> For -> create new Equipment_Maintenance_Item__c
With 0 checks to see if this new Equipment_Maintenance_Item__c was needed.
I ended up partly re-writing the class but the main change was re-working the section of code above to
List<Equipment_Maintenance_Item__c> equipmentMaintainanceList = [SELECT Maintenance_Request__c,
Equipment__r.Id
FROM Equipment_Maintenance_Item__c
Where Maintenance_Request__c IN :closedCaseList];
List<Equipment_Maintenance_Item__c> updatedEquipment = new List<Equipment_Maintenance_Item__c>();
for(Case closed : closedCaseList){
for(Equipment_Maintenance_Item__c eqip : equipmentMaintainanceList){
if (eqip.Maintenance_Request__c == closed.Id){
Equipment_Maintenance_Item__c e = eqip.clone(false, false, false, false);
e.Maintenance_Request__c = oldToNewMap.get(closed.Id).Id;
updatedEquipment.add(e);
}
}
}
if (updatedEquipment.size() > 0 ){
insert updatedEquipment;
}
For me the lesson is be very mindful of nested for loops when the outcome is going to come up against some hard and fast governor limits.
I am working on a lightning component that will display a custom, dynamic, list of 'related' records that can be used to display related records in a list on an object that is ALSO a child of the same parent. So, for example, If the Account is the parent record, and custom object 1 and custom object 2 are both children of the account, I want to display the custom object 1 records that are associated with the same account as custom object 2 on the custom object 2 lightning record page. I am using user inputs in the LC canvas and field sets to locate the records and display the columns, respectively.
The component I created works in that it does query and display the records, columns, and field values dynamically. I wanted to also include a clickable link to the custom object 1 records using the 'Name' field. However, I can't seem to dynamically set the typeAttribute of the Name field to display the label of the record. Because I am assigning it as an attribute of the column header, it is only taking the last value in the array.
Is there a way to assign the typeAttribute to the records response, and NOT the column header response?
Here is my code:
Apex Class:
public class DynamicRelatedListController {
#AuraEnabled
public static String fetchParent(String recId, String parentLookup, String objPageName) {
String strSOQL = 'SELECT Id, ' + parentLookup + ' FROM ' + objPageName + ' WHERE Id = \'' + recId + '\' LIMIT 1 ' ;
List <SObject> currentRecord = new List <SObject>(Database.query(strSOQL));
system.debug('Database Result for fetch parent ' + strSOQL);
List <String> parentIds = new List<String>();
for(SObject cr : currentRecord) {
parentIds.add(String.valueOf(cr.get(parentLookup)));
}
String parentId = parentIds[0];
return parentId;
}
#AuraEnabled
public static List < SObject > fetchChildren(String objectName, String criteria, String parentFieldAPIName, String recId, String parentLookup, String objPageName) {
String parentId = fetchParent(recId, parentLookup, objPageName);
// String prefix ='%' + GM_Util.findObjectNameFromRecordIdPrefix(parentId) + '%';
List < SObject > childRecordList = new List < SObject > ();
String strSOQL = 'SELECT Id, ' + parentFieldAPIName + ' FROM ' + objectName + ' WHERE ' + parentFieldAPIName + ' = \'' + parentId + '\'';
if ( String.isNotBlank( criteria ) )
strSOQL += ' ' + criteria;
childRecordList = Database.query(strSOQL);
system.debug('Database Result for fetch children ' + strSOQL);
return childRecordList;
}
#AuraEnabled
public static DataTableDetails fetchRelatedRecs(String recId, String fieldSetName, String criteria, String objectName, String parentFieldAPIName, String objPageName, String parentLookup)
{
DataTableDetails dataTableDtls = new DataTableDetails();
List<GM_Util_Aura.FieldSetMemberWrapperClass> listOfFieldSetMembers = new List<GM_Util_Aura.FieldSetMemberWrapperClass>();
if(fieldSetName != NULL && fieldSetName != ''){
listOfFieldSetMembers = GM_Util_Aura.getFieldSetMemberJSON(objectName, fieldSetName);
}
// List<GM_Util_Aura.FieldSetMemberWrapperClass> listOfFieldSetMembers = GM_Util_Aura.getFieldSetMemberJSON(objectName, fieldSetName);
List<String> WHERE_IN_LIST = new List <String>();
for (SObject crl: fetchChildren(objectName, criteria, parentFieldAPIName, recId, parentLookup, objPageName)) {
WHERE_IN_LIST.add(crl.Id);
}
String SQL = 'SELECT Id ';
if(listOfFieldSetMembers != null && listOfFieldSetMembers.size() > 0){
for(GM_Util_Aura.FieldSetMemberWrapperClass fsmwc : listOfFieldSetMembers){
dataTableDtls.fieldLabelsList.add(fsmwc);
SQL = SQL + ',' + fsmwc.fieldName;
}
}
SQL += ' FROM ' + objectName +' WHERE Id IN : WHERE_IN_LIST' ;
System.debug('final SQL statement (WHERE_IN_LIST): ' + SQL);
dataTableDtls.returnedParentRecords = Database.query(SQL);
System.debug('returned Parent Records are ' + Database.Query(SQL));
// filteredResults = Database.query(SQL);
System.debug('final filtered results are: ' + dataTableDtls);
System.debug('returned field labels are ' + listOfFieldSetMembers);
return dataTableDtls;
}
public class DataTableDetails{
#AuraEnabled
public List<sObject> returnedParentRecords = new List<sObject>();
#AuraEnabled
public List<GM_Util_Aura.FieldSetMemberWrapperClass> fieldLabelsList = new List<GM_Util_Aura.FieldSetMemberWrapperClass>();
}
}
My component helper & cmp code:
({
getDataTable: function(component, event) {
console.log("entered Get Data Table");
var action = component.get("c.fetchRelatedRecs");
action.setParams({
recId: component.get("v.recordId"),
objectName: component.get("v.ObjectAPIName"),
fieldSetName: component.get("v.FieldSetAPIName"),
objPageName: component.get("v.sObjectName"),
parentFieldAPIName: component.get("v.ParentFieldAPIName"),
parentLookup: component.get("v.ParentFieldLookupName"),
criteria: component.get("v.criteria")
});
action.setCallback(this, function(response){
//console.log("this is the fieldSetName " + fieldSetName)
var state = response.getState();
if(state === 'SUCCESS'){
var fieldSetResponse = response.getReturnValue().fieldLabelsList;
var parentRecordsResponse = response.getReturnValue().returnedParentRecords;
console.log("Parent records length " + parentRecordsResponse.length);
console.log("Field Set Length " + fieldSetResponse.length);
console.log("here are the field labels before data transformation " + JSON.stringify(fieldSetResponse));
console.log("Here are all the related records before transformation " + JSON.stringify(parentRecordsResponse));
//this for loop changes all lookup fields to type 'url' for clickable links
for(var i = 0; i < parentRecordsResponse.length; i++ ) {
if(fieldSetResponse[i].fieldName.includes('__r.Name')) {
fieldSetResponse[i].type ='url';
//drop the .Name from the lookup field so that the lightning:datatable can find the field value in the parentRecordsResponse object
fieldSetResponse[i].fieldName = fieldSetResponse[i].fieldName.replace('.Name', '') ;
//this for loop locates the key in the parentRecordsResponse object that matches the lookup field in the fieldSetResponse object.
for(var j = 0; j < parentRecordsResponse.length; j++) {
var arraykeys = Object.keys(parentRecordsResponse[j]);
var arrayvalues = Object.values(parentRecordsResponse[j]);
console.log("Array Keys in iteration "+ arraykeys[j]);
console.log("Array Values " + JSON.stringify(arrayvalues));
//this for loop locates the key in the parentRecordsResponse object that matches the lookup field in the fieldSetResponse object.
for(var h = 0; h <arraykeys.length; h++) {
console.log("Array Keys in iteration h "+ arraykeys[h]);
//fieldSetResponse[i]['typeAttributes'] = {label: arrayvalues[h].Name };
if(fieldSetResponse[i].fieldName === arraykeys[h]){
//assign the'type attributes' to the url field with the Name value of the lookup field
fieldSetResponse[i]['typeAttributes'] = {label: arrayvalues[h]['Name'] };
//transform the nested lookup field object to /recordId for the url
parentRecordsResponse[j][arraykeys[h]] = "/" + arrayvalues[h].Id ;
}
}
}
}
}
console.log("here are the field labels after data transformation " + JSON.stringify(fieldSetResponse));
console.log("Here are all the related records " + JSON.stringify(parentRecordsResponse));
//send the responses back to the controller and set them using the 'v.' keys
component.set("v.columnsHeader", fieldSetResponse);
component.set("v.listofRelatedRecords", parentRecordsResponse);
}
else if (state === 'ERROR'){
console.log('::::::::::::: ERROR :::::::::::::');
}
});
$A.enqueueAction(action);
}
})
<aura:component controller="DynamicRelatedListController" implements="flexipage:availableForRecordHome,force:hasRecordId,force:lightningQuickAction,force:hasSObjectName" access="global">
<aura:handler name="init" value="{!this}" action="{!c.doInit}" />
<aura:attribute name="ObjectAPIName" type="String"/>
<aura:attribute name="FieldSetAPIName" type="String"/>
<aura:attribute name="ParentFieldAPIName" type="String"/>
<aura:attribute name="ParentFieldLookupName" type="String"/>
<aura:attribute name="Criteria" type="String"/>
<aura:attribute name="TableName" type="String"/>
<!-- <aura:attribute name="recordId" type="String" /> -->
<aura:attribute name="columnsHeader" type="List"/>
<aura:attribute name="listofRelatedRecords" type="Object"/>
<lightning:card aura:id="lightCard" class="slds-card_boundary" title="{!v.TableName}" iconName="standard:file">
<div style="overflow-x: auto;">
<lightning:datatable data="{!v.listofRelatedRecords}"
columns="{!v.columnsHeader}"
keyField="Id"
hideCheckboxColumn="true"/>
</div>
</lightning:card>
</aura:component>
This is what the end result looks like - note that all of the values ARE different, except for the label of the Name field. The link itself works fine, and navigates to the appropriate record:
I figured it out. At a basic level, this line is missing the attribute of 'fieldName', which takes the API of the field name and applies the value.
So this: fieldSetResponse[i]['typeAttributes'] = {label: arrayvalues[h]['Name'] };
Should be this: fieldSetResponse[i]['typeAttributes'] = {label: fieldName: {API NAME OF FIELD THAT HOLDS THE DISPLAY VALUE} };
Even so, this is not possible to do dynamically because When the fieldName in this case is the lookup field itself, it displays the record Id. I could pass a hard-coded API name, but obviously that would defeat the purpose.
To get around this, I added a generic label (click to view record) and created a formula field on that target object that pulls in the parent record name and added that to my field set.
This is a known issue: https://trailblazer.salesforce.com/ideaView?id=0873A000000lLXYQA2
That could potentially be handled by creating a flattening function (although I haven't tried to implement that yet): https://developer.salesforce.com/forums/?id=9062I000000XtwnQAC
I am new to salesforce and I am stuck with a situation here.
I have a class which is scheduled every hour. I hit an account with the below code and an email is sent out to MAROPOST (Marketing automation tool). When this happen I want to track the Account and create a case or a log which says Welcome Email is sent so that I don't hit the same Account again.
Please help. Below is the working class. Please help
public class PD_WelcomeMaroPost {
public static string sendEmailThroughMaro(string myInpEmail) {
string successContacts = '';
string failureContacts = '';
// SQL to fetch FBO who Joined Today
list<Account> conts = new list<Account> ([SELECT name, Email_FLP_com__c,
(SELECT Id
FROM Stripe_Subscriptons__r
WHERE Start_Date__c= TODAY
AND Status__c='active'
AND Welcome_Email__C = false
LIMIT 1)
from account
where ID IN (
select Distributor__c
from Stripe_Subscripton__c
where Start_Date__c= TODAY
AND Status__c='active'
AND Welcome_Email__C = false)
AND Email_FLP_com__c != NULL
LIMIT 100]);
system.debug('>>>>>>>>>>' + conts);
overallEmail myEmail = new overallEmail();
List<Stripe_Subscripton__c> subsToUpdate = new List<Stripe_Subscripton__c>();
for(Account c : conts){
myEmail.email.campaign_id = 172;
myEmail.email.contact.Email = c.Email_FLP_com__c;
myEmail.email.contact.first_name = c.name;
/**MAp<String, String> tags = new Map<String, String>();
tags.put('firstName', c.name);
myEmail.email.tags = tags;**/
system.debug('#### Input JSON: ' + JSON.serialize(myEmail));
try{
String endpoint = 'http://api.maropost.com/accounts/1173/emails/deliver.json?auth_token=j-V4sx8ueUT7eKM8us_Cz5JqXBzoRrNS3p1lEZyPUPGcwWNoVNZpKQ';
HttpRequest req = new HttpRequest();
req.setEndpoint(endpoint);
req.setMethod('POST');
req.setHeader('Content-type', 'application/json');
req.setbody(JSON.serialize(myEmail));
Http http = new Http();
system.debug('Sending email');
HTTPResponse response = http.send(req);
system.debug('sent email');
string resultBodyGet = '';
resultBodyGet = response.getBody();
system.debug('Output response:' + resultBodyGet);
maroResponse myMaroResponse = new maroResponse();
myMaroResponse = (maroResponse) JSON.deserialize(resultBodyGet, maroResponse.class);
system.debug('#### myMaroResponse: ' + myMaroResponse);
if(myMaroResponse.message == 'Email was sent successfully')
successContacts = successContacts + ';' + c.Email_FLP_com__c;
else
failureContacts = failureContacts + ';' + c.Email_FLP_com__c;
}
catch (exception e) {
failureContacts = failureContacts + ';' + c.Email_FLP_com__c;
system.debug('#### Exception caught: ' + e.getMessage());
}
c.Stripe_Subscriptons__r[0].Welcome_Email__c = true;
subsToUpdate.add(c.Stripe_Subscriptons__r[0]);
}
Update subsToUpdate;
return 'successContacts=' + successContacts + '---' + 'failureContacts=' + failureContacts;
}
public class maroResponse {
public string message {get;set;}
}
public class overallEmail {
public emailJson email = new emailJson();
}
public class emailJson {
public Integer campaign_id;
public contactJson contact = new contactJson();
// Public Map<String, String> tags;
}
public class contactJson {
public string email;
public string first_name;
}
}
You're making a callout in a loop, there's governor limit of max 100 callouts. See Limits class to obtain current & max numbers programatically rather than hardcoding it.
Other than that it should be pretty simple change. First add your filter to the query and add a "subquery" (something like a JOIN) that pulls the related list of subscriptions
list<Account> conts = new list<Account> ([SELECT name, Email_FLP_com__c,
(SELECT Id
FROM Stripe_Subscriptions__r
WHERE Start_Date__c= TODAY
AND Status__c='active'
AND Welcome_Email__C = false
LIMIT 1)
from account
where ID IN (
select Distributor__c
from Stripe_Subscripton__c
where Start_Date__c= TODAY
AND Status__c='active'
AND Welcome_Email__C = false)
AND Email_FLP_com__c != NULL
LIMIT 100]);
Then it's just few lines more
List<Stripe_Subscription__c> subsToUpdate = new List<Stripe_Subscription__c>();
for(Account a : conts){
// do your maropost code here
a.Stripe_Subscriptions__r[0].Welcome_Email__c = true;
subsToUpdate.add(a.Stripe_Subscriptions__r[0]);
}
update subsToUpdate;
Of course you might want to set that checkbox to true only if callout went OK ;)
After reading your code, I don't see where you tried to accomplish this. If you post your attempt I'd be glad to help fix it.
Instead I'll give you different logic for what you are trying to do.
1.) create new checkbox field
2.) in batch query where box is not checked
3.) send email
4.) check checkbox
to answer your comment here is some sample code, you will need to fix it yourself, i am just making temp names
for(sobjectname gg:[your query]){
Send email;
gg.checkbox = checked;
update gg;
}
it'd be better to make it bulkified though
list<yourSObject> tobeupdated = new list<yourSObject>([Your query]);
for(yourSObject gg: tobeupdated){
send email;
gg.checkbox = true;
}
update tobeupdated;
I'm trying this code for dynamic results but it's showing the error shown below. Kindly help me with this code.
System.QueryException: Only variable references are allowed in dynamic SOQL/SOSL.
boolean first = true;
string database_query = '';
List<Schema.FieldSetMember> userfields = SObjectType.Task__c.FieldSets.Search.getFields();
for(Schema.FieldSetMember f : userfields) {
if (!first) {
database_query += ', ';
}
first = false;
database_query += f.getFieldPath();
}
if(Search_value != '' && database_query != ''){
TaskList = Database.query('select ' + database_query +',SOE__c from task__c where SOE__c =: \''+selectSOE +'\' and '+ selectedfield + ' like \'%' + Search_value +'%\'');
}
Please try to replace the ':=' with just '=' in your query. Also have a look at the answer to this question, I think you have similar problem:
Query using string not working
My company has an app that they got off of the app exchange(Note: before I started) that allowed you to follow or unfollow large number of cases/accounts/opportunities etc in salesforce.com. Supposedly it worked before and now it isn't working. I need an idea of what is wrong with the code for each button. If I can't fix them, any ideas for a replacement app? The app is no longer on the app exchange any more.
here's the code for the follow button:
{!REQUIRESCRIPT("/soap/ajax/18.0/connection.js")}
//EDIT THE FOLLOWING LINE TO ALTER THE CODE FOR OTHER OBJECTS. USE THE PICKLISTS ABOVE TO SELECT FIELD TYPE = $ObjectType AND THE OBJECT NAME THEN REPLACE "$ObjectType.Case" WITH YOUR NEW OBJECT NAME
var records = {!GETRECORDIDS( $ObjectType.Case)};
function LBox() {
var box = new parent.SimpleDialog("steve"+Math.random(), true);
parent.box = box;`
box.setTitle("Follow Records");
box.createDialog();
box.setWidth(220);
box.setContentInnerHTML("<img src='/img/loading32.gif' alt='' /> Running");
box.setupDefaultButtons();`
box.show();
}
function CBox(){
box.setContentInnerHTML("You are now following "+follow_count+" records<br /><br />Close");
}
if (records[0] == null) {
alert("Please select at least one record.");
}
else {
var follow_count = 0;
LBox();
for (var i = 0; i < records.length; i++){
var fol=new sforce.SObject("EntitySubscription");
fol.ParentId = records[i];
fol.SubscriberId = '{!User.Id}';
try{
sforce.connection.create([fol]);
follow_count++;
}
catch(e){
alert(e);
}
}
CBox();
}
here's the unfollow button:
{!REQUIRESCRIPT("/soap/ajax/18.0/connection.js")}
// EDIT THE FOLLOWING LINE TO ALTER THE CODE FOR OTHER OBJECTS. USE THE PICKLISTS ABOVE TO SELECT FIELD TYPE = $ObjectType AND THE OBJECT NAME THEN REPLACE "$ObjectType.Case" WITH YOUR NEW OBJECT NAME
var records = {!GETRECORDIDS( $ObjectType.Case)};
// display running message popup
function LBox() {
var box = new parent.SimpleDialog("steve"+Math.random(), true);
parent.box = box;`
box.setTitle("Unfollow Records");
box.createDialog();
box.setWidth(220);
box.setContentInnerHTML("<img src='/img/loading32.gif' alt='' /> Running");
box.setupDefaultButtons();
box.show();
}
// display output message
function CBox(){
if (unfollow_count < records.length)
box.setContentInnerHTML("You have now unfollowed "+unfollow_count+" records. You were not following the other selected records. <br /><br />Close");
else
box.setContentInnerHTML("You have now unfollowed "+unfollow_count+" records. <br /><br />Close");
}
if (records[0] == null) {
alert("Please select at least one record.");
}
else {
var unfollow_count = 0;`
LBox();
try {
// find following records
var searchstring = "SELECT Id FROM EntitySubscription WHERE (ParentId IN (";
for (var i = 0; i < records.length - 1; i++) {
searchstring += "'" + records[i] + "',";
}
searchstring += "'" + records[records.length - 1] + "') AND SubscriberId ='{!User.Id}')";
var resultRecords = sforce.connection.query(searchstring).getArray("records");
// delete following records
var recordIds = [];
for (var i = 0; i < resultRecords.length; i++) {
recordIds.push(resultRecords[i].Id);
unfollow_count++;
}
sforce.connection.deleteIds(recordIds);
} catch(e) {
alert(e);
}
CBox();
}
The first error message has to do with permissions, I don't get this error because I have admin rights, the second error is only on the account tab's button. I'm more worried about the permissions problem, is there anything there about permissions. Any help is great!
I think the issue is a throttle in SFDC, you are limited to following only a certain number of records per user. If this app was designed as you described to mass-follow records, it's possible your hitting that throttle, which is kind of the impression I get from the error your co-worker is receiving about at most 1000, also if it worked before, it would make sense that you have maxed out a limit for the org/user
Queries on EntitySubscription by users who aren't system admins must contain a LIMIT. If you change the query code in the button to the following it should work:
// find following records
var searchstring = "SELECT Id FROM EntitySubscription WHERE (ParentId IN (";
for (var i = 0; i < records.length - 1; i++) {
searchstring += "'" + records[i] + "',";
}
searchstring += "'" + records[records.length - 1] + "') AND SubscriberId ='{!User.Id}') LIMIT 1000";