-
ChatterFeed
-
0Best Answers
-
0Likes Received
-
0Likes Given
-
4Questions
-
8Replies
Future Method Switching Between System and User Context
I feel like I'm going crazy with a future method that was running in user context this morning, then switched to system context in the afternoon, and now just switched back to user context again. I have not made any updates or code changes so I'm pretty baffled.
Background:
I have a trigger/class that updates fields on Contacts associated with Customer Community User records and also uses a Future method to update the user profile depending on which type of Account they are placed. This is triggered when a user updates the Account Name field on the Contact record. Everything runs perfect except when the Future method is called. If the user that invoked the trigger/class does not have "Manage Users" enabled on "System Permissions" in their profile then they get the following error: You do not have the level of access necessary to perform the operation you requested. Please contact the owner of the record or your administrator if access is necessary.
I've tried configuring the class that contains the Future method as both "public class" and "public without sharing class", but still the Future method seems to run in User Context and users get that error. The terrible solution, that has proven successful, is to grant those users the profile permission of "Manage Users", but that is much to dangerous to grant to non-admins. All of the other permissions in the "System Permissions" section of "Users" must also be selected for that to be enabled on the profile and that is crazy to give to regular users.
Questions:
Background:
I have a trigger/class that updates fields on Contacts associated with Customer Community User records and also uses a Future method to update the user profile depending on which type of Account they are placed. This is triggered when a user updates the Account Name field on the Contact record. Everything runs perfect except when the Future method is called. If the user that invoked the trigger/class does not have "Manage Users" enabled on "System Permissions" in their profile then they get the following error: You do not have the level of access necessary to perform the operation you requested. Please contact the owner of the record or your administrator if access is necessary.
I've tried configuring the class that contains the Future method as both "public class" and "public without sharing class", but still the Future method seems to run in User Context and users get that error. The terrible solution, that has proven successful, is to grant those users the profile permission of "Manage Users", but that is much to dangerous to grant to non-admins. All of the other permissions in the "System Permissions" section of "Users" must also be selected for that to be enabled on the profile and that is crazy to give to regular users.
Questions:
- Do Future methods run in User Context or System Context?
- How can I guarantee that a Future method will always run in System Context?
- What about the way these asynchornous methods or Future methods work that causes it to alternate it's behavior when it comes to which context it runs in (even in multiple sandboxes where no code changes were made)?
- RedmossSubC4i
- August 19, 2016
- Like
- 0
Live Agent - Pre Chat Form Custom Case Picklists - Display Visitor Name Before Accept Chat
I've gone through the development documentation for Live Agent and need help with the following:
Background:
We are currently using the Customer Community and have the Live Agent chat request button located inside the community.
1) Display visitor name in Chat Monitor for Engaged and Chat Requests before agent accepts the chat. Our agents want to see the person's name before they accept the chat. My current approach includes a custom controller for the visualforce page that gets the current users info.
2) Use custom picklist fields from Case object (including those with dependent picklists) on Pre Chat Form and include that data to create a new case.
3) Allow chat visitor to search for existing case number on Pre Chat Form and link to existing case if found.
It's messy, but here's what I have so far.
Background:
We are currently using the Customer Community and have the Live Agent chat request button located inside the community.
1) Display visitor name in Chat Monitor for Engaged and Chat Requests before agent accepts the chat. Our agents want to see the person's name before they accept the chat. My current approach includes a custom controller for the visualforce page that gets the current users info.
2) Use custom picklist fields from Case object (including those with dependent picklists) on Pre Chat Form and include that data to create a new case.
3) Allow chat visitor to search for existing case number on Pre Chat Form and link to existing case if found.
It's messy, but here's what I have so far.
<apex:page id="CommunityPreChatForm" docType="html-5.0" standardcontroller="Case" extensions="CommunityPreChatController" showHeader="false"> <script type='text/javascript'> (function() { function handlePageLoad(){ var endpointMatcher = new RegExp("[\\?\\&]endpoint=([^&#]*)"); document.getElementById('prechatForm').setAttribute('action', decodeURIComponent(endpointMatcher.exec(document.location.search)[1])); } if (window.addEventListener){ window.addEventListener('load', handlePageLoad, false); } else {window.attachEvent('onload', handlePageLoad, false);} })(); </script> <img src="{!$Resource.Live_Agent_Logo}"/><p/> <apex:outputText style="font-weight:bold;font-size:14px;color:#595959;padding:0 10px;" value="Chat with Technical Support"/><p/> <div id="chatFormDiv"> <!-- Get chat visitor info and set for Live Agent --> <form method='post' id='prechatForm'> <apex:form> <div style="width:600px; height:auto; float:left; text-align:left;"> <apex:outputText style="font-weight:bold;font-size:12px;color:#595959;float:left;padding:0 10px;" value="Product:"/> <apex:inputField value="{!Case.Affected_Product__c}" styleClass="picklist" required="true"></apex:inputField> </div><br/> <div style="width:600px; height:auto; float:left; text-align:left;"> <apex:outputText style="font-weight:bold;font-size:12px;color:#595959;float:left;padding:0 10px;" value="Version:"/> <apex:inputField value="{!Case.Version__c}" styleClass="picklist" required="true"></apex:inputField> </div><br/> <div style="width:600px; height:auto; float:left; text-align:left;"> <apex:outputText style="font-weight:bold;font-size:12px;color:#595959;float:left;padding:0 10px;" value="Category:"/> <apex:inputField value="{!Case.Request_Type__c}" styleClass="picklist" required="true"></apex:inputField> </div><br/> <div style="width:600px; height:auto; float:left; text-align:left;"> <apex:outputText style="font-weight:bold;font-size:12px;color:#595959;float:left;padding:0 10px;" value="Topic:"/> <apex:inputField value="{!Case.Problem_Type__c}" styleClass="picklist" required="true"></apex:inputField><p/> </div><br/> <apex:outputText style="font-weight:bold;font-size:12px;color:#595959;float:left;padding:0 10px;" value="Provide a brief describe of the issue:"/> <apex:inputField value="{!Case.Subject}" styleClass="textarea" html-placeholder="How we can help?" required="true"></apex:inputField> <!-- Hidden Collection Fields --> <input type="hidden" name="liveagent.prechat:ContactFirstName" id="firstName" value="{!contactFirstName}"/> <input type="hidden" name="liveagent.prechat:ContactLastName" id="lastName" value="{!contactLastName}"/> <input type="hidden" name="liveagent.prechat:ContactEmail" id="email" value="{!contactEmail}"/> <input type="hidden" name="liveagent.prechat:CaseSubject" id="subject" value="{!caseSubject}}"/> <input type="hidden" name="liveagent.prechat:CaseDescription" id="description" value="{!Case.Description}"/> <input type="hidden" name="liveagent.prechat:CaseStatus" value="New"/> <input type="hidden" name="liveagent.prechat:CaseOrigin" value="Live Chat"/> <input type="hidden" name="liveagent.prechat:CaseRecordType" value="012F0000000zp0x"/> <input type="hidden" name="liveagent.prechat.name" id="prechat_field_name"/> <!-- Map Contact Fields via Form --> <input type="hidden" name="liveagent.prechat.findorcreate.map:Contact" value="FirstName,ContactFirstName;LastName,ContactLastName;Email,ContactEmail"/> <!-- Map Case Fields via Form --> <input type="hidden" name="liveagent.prechat.findorcreate.map:Case" value="Subject,CaseSubject;Status,CaseStatus;Origin,CaseOrigin;Description,CaseSubject;Request_Type__c,CaseRequestType"/> <!-- Find Contact and Match or Create --> <input type="hidden" name="liveagent.prechat.findorcreate.map.doFind:Contact" value="Email,true"/> <input type="hidden" name="liveagent.prechat.findorcreate.map.isExactMatch:Contact" value="Email,true"/> <input type="hidden" name="liveagent.prechat.findorcreate.map.doCreate:Contact" value="FirstName,true;LastName,true;Email,true;Phone,true"/> <!-- Create Case and attach Chat Details --> <input type="hidden" name="liveagent.prechat.findorcreate.map.doCreate:Case" value="Subject,true;Status,true;Origin,true"/> <!-- Link Contact to New Case --> <input type="hidden" name="liveagent.prechat.findorcreate.linkToEntity:Contact" value="Case,ContactId"/> <!-- Display Contact and Case as sub-tabs in Live Agent Console --> <input type="hidden" name="liveagent.prechat.findorcreate.showOnCreate:Contact" value="true"/> <input type="hidden" name="liveagent.prechat.findorcreate.showOnCreate:Case" value="true"/> <!-- Link Chat Transcript to Contact and Case --> <input type="hidden" name="liveagent.prechat.findorcreate.saveToTranscript:Contact" value="ContactId"/> <input type="hidden" name="liveagent.prechat.findorcreate.saveToTranscript:Case" value="CaseId"/> <!-- Hide Case Record Type from Agent --> <input type="hidden" name="liveagent.prechat.findorcreate.displayToAgent:CaseRecordType" value="false"/> <!-- Start Chat Button --> <p/><input type='submit' value='REQUEST A CHAT' id='prechat_sbumit' onclick="setName()"/> </apex:form> <!-- Set Chat Visitor name in Live Agent Console --> <script type="text/javascript"> function setName(){ document.getElementById("prechat_field_name").value = document.getElementById("firstName").value + " " + document.getElementById("lastName").value; } </script> <!-- CSS --> <style> .picklist{ width:300px; height:20px; background: #fff; border: 1px solid #CCCCCC; border-radius: 5px; margin-bottom: 5px; padding: 0 10px; } select{ width:300px; height:20px; background: #fff; border: 1px solid #CCCCCC; border-radius: 5px; margin-bottom: 5px; padding: 0 10px; } .textarea{ width:450px; height: 100px; background: #fff; border: 1px solid #CCCCCC; border-radius: 5px; margin-bottom: 5px; padding: 0 10px; } </style> <style> body{ background-color:#fff; } #chatFormDiv { width:300px; text-align:center; padding:5px; } #chatHeader { color:#6d6d6d; font-size:18px; font-weight:bold; float: center; } #prechat_submit { font-weight:bold; float: center; } label { width:150px; font-weight:bold; } input[type=text], textarea { height: 50px; width:400px; background: #fff; border: 1px solid #CCCCCC; border-radius: 5px; margin-bottom: 5px; padding: 0 10px; } input[type=email] { height: 30px; width:280px; background: #fff; border: 1px solid #CCCCCC; border-radius: 3px; margin-bottom: 5px; padding: 0 10px; } input[type=tel] { height: 30px; width:280px; background: #fff; border: 1px solid #CCCCCC; border-radius: 3px; margin-bottom: 5px; padding: 0 10px; } input[type=text] { height: 30px; } textarea{ height:140px; padding-top: 10px; padding-bottom: 10px; } .chatStatusDiv { display:none; } </style> </form> </div> </apex:page>
- RedmossSubC4i
- May 11, 2016
- Like
- 0
Bulkification Issue with Apex Program for Opportunity Tracking
I've written an apex program to capture opportunity metrics via timestamps. The goal is to calculate how long it takes a sales rep to reach certain opportunity amount thresholds and how long it takes them to get their first win and first lost opportunity. The amount thresholds are cumulative across multiple opportunities owned by each rep. The program seems to be working when activating the opportunity trigger for one opportunity/owner at a time, but jumbles the data when mass updating many opportunities owned by many users. All of the results are being store in a custom object called Sales Rep Ramp.
Any help at least identifying what the issue is will be greatly appreciated. Still working on it, but it may be during all the sorting, the nested loops, or the static collections in the class or something else. Thank you in advance!
Apex class code is below:
Any help at least identifying what the issue is will be greatly appreciated. Still working on it, but it may be during all the sorting, the nested loops, or the static collections in the class or something else. Thank you in advance!
Apex class code is below:
public class SalesRepRamp { //Recursive Control - Before Trigger public static boolean hasAlreadyRunBefore = false; public static boolean hasAlreadyRunMethodBefore(){ return hasAlreadyRunBefore; } public static void setAlreadyRunMethodBefore(){ hasAlreadyRunBefore = true; } //Recursive Control - After Trigger public static boolean hasAlreadyRunAfter = false; public static boolean hasAlreadyRunMethodAfter(){ return hasAlreadyRunAfter; } public static void setAlreadyRunMethodAfter(){ hasAlreadyRunAfter = true; } //Reference static Set<Id> userIds = new Set<Id>(); static Map<Id,User> userMap = new Map<Id,User>(OpportunitySharedResources.userMap); static Map<Id,Opportunity> oppMap = new Map<Id,Opportunity>(); static Map<String,Sales_Rep_Ramp__c> salesRampMap = new Map<String,Sales_Rep_Ramp__c>(); //Map<UserId+SalesRepRampType,Sales_Rep_Ramp__c> //First Win or First Loss static Map<Id,Map<Date,Opportunity>> userFirstCloseOppMap = new Map<Id,Map<Date,Opportunity>>(); //Map<UserId,Map<CloseDate,Opportunity>> static Map<Id,List<Date>> userFirstCloseDateMap = new Map<Id,List<Date>>(); //Map<UserId,List<CloseDate>> //Pipeline Tracking static Map<Id,Map<Datetime,Opportunity>> userPipelineOppMap = new Map<Id,Map<Datetime,Opportunity>>(); //Map<UserId,Map<Last_Software_Appliance_Modified_Date__c, Opportunity>> static Map<Id,List<Datetime>> userPipelineDatetimeMap = new Map<Id,List<Datetime>>(); //Map<UserId,List<Last_Software_Appliance_Modified_Date__c>> //Results static Map<String,Datetime> thresholdMap = new Map<String,Datetime>(); static Map<Id,Map<String,Datetime>> userResultsMap = new Map<Id,Map<String,Datetime>>(); //Map<UserId,Map<SalesRepRampType,Last_Software_Appliance_Modified_Date__c>> static List<Sales_Rep_Ramp__c> insertSRR = new List<Sales_Rep_Ramp__c>(); public static void entryMethod(List<Opportunity> newList, Map<Id,Opportunity> oldMap, String triggerType){ if(triggerType == 'Before Insert' || triggerType == 'Before Update'){ lastAmountTimestamp(newlist, oldMap, triggerType); adjustClosedLostDate(newList, oldMap, triggerType); } if(triggerType == 'After Insert' || triggerType == 'After Update'){ //Only process Opportunities owned by new sales reps for(Opportunity o: newList){ if(userMap.containsKey(o.OwnerId) && userMap.get(o.OwnerId).Sales_Rep_Ramp__c == true && userMap.get(o.OwnerId).Created_Date__c != null){ userIds.add(o.OwnerId); } } } if(userIds.size() > 0){ getSalesRepRamp(); getRelatedOpps(); processRelatedOpps(newList,oldMap); if(userResultsMap.size() > 0){ updateSalesRepRamp(); } } } public static void lastAmountTimestamp(List<Opportunity> newList, Map<Id,Opportunity> oldMap, String triggerType){ if(triggerType == 'Before Update'){ for(Opportunity o: newList){ Opportunity oldOpp = oldMap.get(o.Id); if(o.Total_Software_and_Appliance__c != oldOpp.Total_Software_and_Appliance__c){ o.Last_Software_Appliance_Modified_Date__c = datetime.now(); } } } } public static void adjustClosedLostDate(List<Opportunity> newList, Map<Id,Opportunity> oldMap, String triggerType){ if(triggerType == 'Before Update'){ for(Opportunity o:newList){ Opportunity oldOpp = oldMap.get(o.Id); if((o.StageName == 'Closed Lost' || o.StageName == 'Closed - Deferred') && oldOpp.StageName != o.StageName && oldOpp.StageName != 'Closed Lost' && oldOpp.StageName != 'Closed - Deferred'){ o.CloseDate = date.today(); } } } } public static void getSalesRepRamp(){ List<Sales_Rep_Ramp__c> salesRampList = new List<Sales_Rep_Ramp__c>([SELECT Id, User__c, Type__c, Timestamp__c FROM Sales_Rep_Ramp__c WHERE User__c IN: userIds]); for(Sales_Rep_Ramp__c srr: salesRampList){ String uniqueKey = srr.User__c+srr.Type__c; salesRampMap.put(uniqueKey,srr); } } public static void getRelatedOpps(){ List<Opportunity> oppList = new List<Opportunity>([ SELECT Id, Name, OwnerId, CloseDate, CreatedDate, StageName, Total_Software_and_Appliance__c, Last_Software_Appliance_Modified_Date__c, Total_Amount__c, IsClosed, IsWon FROM Opportunity WHERE OwnerId IN: userIds AND Total_Software_and_Appliance__c > 0 AND Last_Software_Appliance_Modified_Date__c != null ]); oppMap.putAll(oppList); } public static void processRelatedOpps(List<Opportunity> newList, Map<Id,Opportunity> oldMap){ //Reset thresholdMap thresholdMap.clear(); for(Opportunity o: oppMap.values()){ /*First Win or First Loss - Setup*/ if(o.IsClosed == true){ //Collect Opp by Rep and Date (Close Date) if(userFirstCloseOppMap.containsKey(o.OwnerId)){ userFirstCloseOppMap.get(o.OwnerId).put(o.CloseDate,o); } else{ Map<Date,Opportunity> OppDateMap = new Map<Date,Opportunity>(); OppDateMap.put(o.CloseDate,o); userFirstCloseOppMap.put(o.OwnerId,OppDateMap); } //Collect opps by Rep into Date lists if(userFirstCloseDateMap.containsKey(o.OwnerId)){ userFirstCloseDateMap.get(o.OwnerId).add(o.CloseDate); } else{ List<Date> OppDateList = new List<Date>(); OppDateList.add(o.CloseDate); userFirstCloseDateMap.put(o.OwnerId,OppDateList); } } /*Pipeline - Setup*/ if(o.IsClosed == false || (o.IsClosed == true && o.IsWon == true)){ //Collect Opps by Rep and Datetime (Last Software Appliance Modified Date) if(userPipelineOppMap.containsKey(o.OwnerId)){ userPipelineOppMap.get(o.OwnerId).put(o.Last_Software_Appliance_Modified_Date__c,o); } else{ Map<Datetime,Opportunity> OppDatetimeMap = new Map<Datetime,Opportunity>(); OppDatetimeMap.put(o.Last_Software_Appliance_Modified_Date__c,o); userPipelineOppMap.put(o.OwnerId,OppDatetimeMap); } //Collect Opps by Rep into Datetime lists if(userPipelineDatetimeMap.containsKey(o.OwnerId)){ userPipelineDatetimeMap.get(o.OwnerId).add(o.Last_Software_Appliance_Modified_Date__c); } else{ List<Datetime> OppDatetimeList = new List<Datetime>(); OppDatetimeList.add(o.Last_Software_Appliance_Modified_Date__c); userPipelineDatetimeMap.put(o.OwnerId,OppDatetimeList); } } } /*First Win or First Loss - Process*/ for(Id repId: userFirstCloseDateMap.keySet()){ List<Date> OppDateList = userFirstCloseDateMap.get(repId); //Sort Close Date by oldest first OppDateList.sort(); //Find Opps with First Win or First Loss for(Date closeDate: OppDateList){ Opportunity o = userFirstCloseOppMap.get(repId).get(closeDate); //First Win if(o.IsClosed == true && o.IsWon == true && !thresholdMap.containsKey('First Win')){ Time myTime = Time.newInstance(5,0,0,0); Datetime timestamp = Datetime.newInstance(closeDate,myTime); setupUserResultsMap(repId, 'First Win', timestamp); } //First Loss if(o.IsClosed == true && o.IsWon == false && !thresholdMap.containsKey('First Loss')){ Time myTime = Time.newInstance(5,0,0,0); Datetime timestamp = Datetime.newInstance(closeDate,myTime); setupUserResultsMap(repId, 'First Loss', timestamp); } } } /*Pipeline - Process*/ for(Id repId: userPipelineDatetimeMap.keySet()){ List<Datetime> OppDatetimeList = userPipelineDatetimeMap.get(repId); //Sort Last Software/Appliance Modified Date by oldest first OppDatetimeList.sort(); //SUM Opp Total Software and Appliance in order from oldest to newest Decimal totalSWandApp = 0; for(Datetime timestamp: OppDatetimeList){ Opportunity o = userPipelineOppMap.get(repId).get(timestamp); if(totalSWandApp > 0){ totalSWandApp = totalSWandApp + o.Total_Software_and_Appliance__c; } else{ totalSWandApp = o.Total_Software_and_Appliance__c; } //Check current total and verify Amount thresholds //50k Pipeline if(totalSWandApp >= 50000 && !thresholdMap.containsKey('50k Pipeline')){ setupUserResultsMap(repId, '50k Pipeline', timestamp); } //100k Pipeline if(totalSWandApp >= 100000 && !thresholdMap.containsKey('100k Pipeline')){ setupUserResultsMap(repId, '100k Pipeline', timestamp); } //150k Pipeline if(totalSWandApp >= 150000 && !thresholdMap.containsKey('150k Pipeline')){ setupUserResultsMap(repId, '150k Pipeline', timestamp); } //200k Pipeline if(totalSWandApp >= 200000 && !thresholdMap.containsKey('200k Pipeline')){ setupUserResultsMap(repId, '200k Pipeline', timestamp); } //250k Pipeline if(totalSWandApp >= 250000 && !thresholdMap.containsKey('250k Pipeline')){ setupUserResultsMap(repId, '250k Pipeline', timestamp); } //400k Pipeline if(totalSWandApp >= 400000 && !thresholdMap.containsKey('400k Pipeline')){ setupUserResultsMap(repId, '400k Pipeline', timestamp); } //500k Pipeline if(totalSWandApp >= 500000 && !thresholdMap.containsKey('500k Pipeline')){ setupUserResultsMap(repId, '500k Pipeline', timestamp); } } } } public static void setupUserResultsMap(Id repId, String Type, Datetime timestamp){ if(userResultsMap.containsKey(repId) && !userResultsMap.get(repId).containsKey(Type)){ userResultsMap.get(repId).put(Type,timestamp); } else{ thresholdMap.put(Type,timestamp); userResultsMap.put(repId,thresholdMap); } } public static Sales_Rep_Ramp__c setupSalesRepRamp(Id repId, String Type){ Sales_Rep_Ramp__c srr = new Sales_Rep_Ramp__c( User__c = repId, Type__c = Type, Timestamp__c = userResultsMap.get(repId).get(Type) ); return srr; } public static void processSalesRepRamp(Id userId, String Type){ String uniqueKey = userId+Type; //Check for existing and only insert new Sales Rep Ramp record if(!salesRampMap.containsKey(uniqueKey) && userResultsMap.containsKey(userId) && userResultsMap.get(userId).containsKey(Type)){ Sales_Rep_Ramp__c srr = setupSalesRepRamp(userId, Type); insertSRR.add(srr); } } public static void updateSalesRepRamp(){ //Refresh insert list insertSRR.clear(); //Setup for each threshold for(Id repId: userResultsMap.keySet()){ //50k Pipeline processSalesRepRamp(repId,'50k Pipeline'); //100k Pipeline processSalesRepRamp(repId,'100k Pipeline'); //150k Pipeline processSalesRepRamp(repId,'150k Pipeline'); //200k Pipeline processSalesRepRamp(repId,'200k Pipeline'); //250k Pipeline processSalesRepRamp(repId,'250k Pipeline'); //400k Pipeline processSalesRepRamp(repId,'400k Pipeline'); //500k Pipeline processSalesRepRamp(repId,'500k Pipeline'); //First Win processSalesRepRamp(repId,'First Win'); //First Loss processSalesRepRamp(repId,'First Loss'); } if(insertSRR.size() > 0){ insert insertSRR; } } }
- RedmossSubC4i
- August 13, 2015
- Like
- 0
AggregateResult - System.ListException: Row with null Id at index: 0
One of my aggregate result SOQL queries is getting the error, System.ListException: Row with null Id at index: 0. If I'm correct this tells me that the query came up with no results. What's confusing is that I have another aggregate result that is super similar that works just fine. Can anyone please help explain how to fix this? Thanks in advance!
Problem code:
//Aggreate SOQL to get next scheduled task
Map<Id,AggregateResult> LDRnextTaskSummary = new Map<Id,AggregateResult>([SELECT WhoId LeadId, MIN(CreatedDate) NextDate FROM Task WHERE (WhoId IN: leadIds) AND (Owner.Profile.Name LIKE '%LDR%') AND (Status = 'Not Started' OR Status = 'In Progress') AND (ActivityDate >= TODAY) GROUP BY WhoId]);
Similar SOQL That Works:
//Aggregate SOQL to get First and Last Tasks
Map<Id,AggregateResult> LDRpastTaskSummary = new Map<Id,AggregateResult>([SELECT WhoId LeadId, MIN(CreatedDate) FirstDate, MAX(CreatedDate) LastDate FROM Task WHERE (WhoId IN :leadIds) AND (Owner.Profile.Name LIKE '%LDR%') AND (Status = 'Completed') GROUP BY WhoId]);
Was testing and this also got the same error:
Map<Id,AggregateResult> LDRnextTaskSummary = new Map<Id,AggregateResult>([SELECT WhoId LeadId, MIN(CreatedDate) NextDate FROM Task WHERE (WhoId IN: leadIds) AND (Owner.Profile.Name LIKE '%LDR%') GROUP BY WhoId]);
Problem code:
//Aggreate SOQL to get next scheduled task
Map<Id,AggregateResult> LDRnextTaskSummary = new Map<Id,AggregateResult>([SELECT WhoId LeadId, MIN(CreatedDate) NextDate FROM Task WHERE (WhoId IN: leadIds) AND (Owner.Profile.Name LIKE '%LDR%') AND (Status = 'Not Started' OR Status = 'In Progress') AND (ActivityDate >= TODAY) GROUP BY WhoId]);
Similar SOQL That Works:
//Aggregate SOQL to get First and Last Tasks
Map<Id,AggregateResult> LDRpastTaskSummary = new Map<Id,AggregateResult>([SELECT WhoId LeadId, MIN(CreatedDate) FirstDate, MAX(CreatedDate) LastDate FROM Task WHERE (WhoId IN :leadIds) AND (Owner.Profile.Name LIKE '%LDR%') AND (Status = 'Completed') GROUP BY WhoId]);
Was testing and this also got the same error:
Map<Id,AggregateResult> LDRnextTaskSummary = new Map<Id,AggregateResult>([SELECT WhoId LeadId, MIN(CreatedDate) NextDate FROM Task WHERE (WhoId IN: leadIds) AND (Owner.Profile.Name LIKE '%LDR%') GROUP BY WhoId]);
- RedmossSubC4i
- April 25, 2014
- Like
- 0
Future Method Switching Between System and User Context
I feel like I'm going crazy with a future method that was running in user context this morning, then switched to system context in the afternoon, and now just switched back to user context again. I have not made any updates or code changes so I'm pretty baffled.
Background:
I have a trigger/class that updates fields on Contacts associated with Customer Community User records and also uses a Future method to update the user profile depending on which type of Account they are placed. This is triggered when a user updates the Account Name field on the Contact record. Everything runs perfect except when the Future method is called. If the user that invoked the trigger/class does not have "Manage Users" enabled on "System Permissions" in their profile then they get the following error: You do not have the level of access necessary to perform the operation you requested. Please contact the owner of the record or your administrator if access is necessary.
I've tried configuring the class that contains the Future method as both "public class" and "public without sharing class", but still the Future method seems to run in User Context and users get that error. The terrible solution, that has proven successful, is to grant those users the profile permission of "Manage Users", but that is much to dangerous to grant to non-admins. All of the other permissions in the "System Permissions" section of "Users" must also be selected for that to be enabled on the profile and that is crazy to give to regular users.
Questions:
Background:
I have a trigger/class that updates fields on Contacts associated with Customer Community User records and also uses a Future method to update the user profile depending on which type of Account they are placed. This is triggered when a user updates the Account Name field on the Contact record. Everything runs perfect except when the Future method is called. If the user that invoked the trigger/class does not have "Manage Users" enabled on "System Permissions" in their profile then they get the following error: You do not have the level of access necessary to perform the operation you requested. Please contact the owner of the record or your administrator if access is necessary.
I've tried configuring the class that contains the Future method as both "public class" and "public without sharing class", but still the Future method seems to run in User Context and users get that error. The terrible solution, that has proven successful, is to grant those users the profile permission of "Manage Users", but that is much to dangerous to grant to non-admins. All of the other permissions in the "System Permissions" section of "Users" must also be selected for that to be enabled on the profile and that is crazy to give to regular users.
Questions:
- Do Future methods run in User Context or System Context?
- How can I guarantee that a Future method will always run in System Context?
- What about the way these asynchornous methods or Future methods work that causes it to alternate it's behavior when it comes to which context it runs in (even in multiple sandboxes where no code changes were made)?
- RedmossSubC4i
- August 19, 2016
- Like
- 0
Live Agent - Pre Chat Form Custom Case Picklists - Display Visitor Name Before Accept Chat
I've gone through the development documentation for Live Agent and need help with the following:
Background:
We are currently using the Customer Community and have the Live Agent chat request button located inside the community.
1) Display visitor name in Chat Monitor for Engaged and Chat Requests before agent accepts the chat. Our agents want to see the person's name before they accept the chat. My current approach includes a custom controller for the visualforce page that gets the current users info.
2) Use custom picklist fields from Case object (including those with dependent picklists) on Pre Chat Form and include that data to create a new case.
3) Allow chat visitor to search for existing case number on Pre Chat Form and link to existing case if found.
It's messy, but here's what I have so far.
Background:
We are currently using the Customer Community and have the Live Agent chat request button located inside the community.
1) Display visitor name in Chat Monitor for Engaged and Chat Requests before agent accepts the chat. Our agents want to see the person's name before they accept the chat. My current approach includes a custom controller for the visualforce page that gets the current users info.
2) Use custom picklist fields from Case object (including those with dependent picklists) on Pre Chat Form and include that data to create a new case.
3) Allow chat visitor to search for existing case number on Pre Chat Form and link to existing case if found.
It's messy, but here's what I have so far.
<apex:page id="CommunityPreChatForm" docType="html-5.0" standardcontroller="Case" extensions="CommunityPreChatController" showHeader="false"> <script type='text/javascript'> (function() { function handlePageLoad(){ var endpointMatcher = new RegExp("[\\?\\&]endpoint=([^&#]*)"); document.getElementById('prechatForm').setAttribute('action', decodeURIComponent(endpointMatcher.exec(document.location.search)[1])); } if (window.addEventListener){ window.addEventListener('load', handlePageLoad, false); } else {window.attachEvent('onload', handlePageLoad, false);} })(); </script> <img src="{!$Resource.Live_Agent_Logo}"/><p/> <apex:outputText style="font-weight:bold;font-size:14px;color:#595959;padding:0 10px;" value="Chat with Technical Support"/><p/> <div id="chatFormDiv"> <!-- Get chat visitor info and set for Live Agent --> <form method='post' id='prechatForm'> <apex:form> <div style="width:600px; height:auto; float:left; text-align:left;"> <apex:outputText style="font-weight:bold;font-size:12px;color:#595959;float:left;padding:0 10px;" value="Product:"/> <apex:inputField value="{!Case.Affected_Product__c}" styleClass="picklist" required="true"></apex:inputField> </div><br/> <div style="width:600px; height:auto; float:left; text-align:left;"> <apex:outputText style="font-weight:bold;font-size:12px;color:#595959;float:left;padding:0 10px;" value="Version:"/> <apex:inputField value="{!Case.Version__c}" styleClass="picklist" required="true"></apex:inputField> </div><br/> <div style="width:600px; height:auto; float:left; text-align:left;"> <apex:outputText style="font-weight:bold;font-size:12px;color:#595959;float:left;padding:0 10px;" value="Category:"/> <apex:inputField value="{!Case.Request_Type__c}" styleClass="picklist" required="true"></apex:inputField> </div><br/> <div style="width:600px; height:auto; float:left; text-align:left;"> <apex:outputText style="font-weight:bold;font-size:12px;color:#595959;float:left;padding:0 10px;" value="Topic:"/> <apex:inputField value="{!Case.Problem_Type__c}" styleClass="picklist" required="true"></apex:inputField><p/> </div><br/> <apex:outputText style="font-weight:bold;font-size:12px;color:#595959;float:left;padding:0 10px;" value="Provide a brief describe of the issue:"/> <apex:inputField value="{!Case.Subject}" styleClass="textarea" html-placeholder="How we can help?" required="true"></apex:inputField> <!-- Hidden Collection Fields --> <input type="hidden" name="liveagent.prechat:ContactFirstName" id="firstName" value="{!contactFirstName}"/> <input type="hidden" name="liveagent.prechat:ContactLastName" id="lastName" value="{!contactLastName}"/> <input type="hidden" name="liveagent.prechat:ContactEmail" id="email" value="{!contactEmail}"/> <input type="hidden" name="liveagent.prechat:CaseSubject" id="subject" value="{!caseSubject}}"/> <input type="hidden" name="liveagent.prechat:CaseDescription" id="description" value="{!Case.Description}"/> <input type="hidden" name="liveagent.prechat:CaseStatus" value="New"/> <input type="hidden" name="liveagent.prechat:CaseOrigin" value="Live Chat"/> <input type="hidden" name="liveagent.prechat:CaseRecordType" value="012F0000000zp0x"/> <input type="hidden" name="liveagent.prechat.name" id="prechat_field_name"/> <!-- Map Contact Fields via Form --> <input type="hidden" name="liveagent.prechat.findorcreate.map:Contact" value="FirstName,ContactFirstName;LastName,ContactLastName;Email,ContactEmail"/> <!-- Map Case Fields via Form --> <input type="hidden" name="liveagent.prechat.findorcreate.map:Case" value="Subject,CaseSubject;Status,CaseStatus;Origin,CaseOrigin;Description,CaseSubject;Request_Type__c,CaseRequestType"/> <!-- Find Contact and Match or Create --> <input type="hidden" name="liveagent.prechat.findorcreate.map.doFind:Contact" value="Email,true"/> <input type="hidden" name="liveagent.prechat.findorcreate.map.isExactMatch:Contact" value="Email,true"/> <input type="hidden" name="liveagent.prechat.findorcreate.map.doCreate:Contact" value="FirstName,true;LastName,true;Email,true;Phone,true"/> <!-- Create Case and attach Chat Details --> <input type="hidden" name="liveagent.prechat.findorcreate.map.doCreate:Case" value="Subject,true;Status,true;Origin,true"/> <!-- Link Contact to New Case --> <input type="hidden" name="liveagent.prechat.findorcreate.linkToEntity:Contact" value="Case,ContactId"/> <!-- Display Contact and Case as sub-tabs in Live Agent Console --> <input type="hidden" name="liveagent.prechat.findorcreate.showOnCreate:Contact" value="true"/> <input type="hidden" name="liveagent.prechat.findorcreate.showOnCreate:Case" value="true"/> <!-- Link Chat Transcript to Contact and Case --> <input type="hidden" name="liveagent.prechat.findorcreate.saveToTranscript:Contact" value="ContactId"/> <input type="hidden" name="liveagent.prechat.findorcreate.saveToTranscript:Case" value="CaseId"/> <!-- Hide Case Record Type from Agent --> <input type="hidden" name="liveagent.prechat.findorcreate.displayToAgent:CaseRecordType" value="false"/> <!-- Start Chat Button --> <p/><input type='submit' value='REQUEST A CHAT' id='prechat_sbumit' onclick="setName()"/> </apex:form> <!-- Set Chat Visitor name in Live Agent Console --> <script type="text/javascript"> function setName(){ document.getElementById("prechat_field_name").value = document.getElementById("firstName").value + " " + document.getElementById("lastName").value; } </script> <!-- CSS --> <style> .picklist{ width:300px; height:20px; background: #fff; border: 1px solid #CCCCCC; border-radius: 5px; margin-bottom: 5px; padding: 0 10px; } select{ width:300px; height:20px; background: #fff; border: 1px solid #CCCCCC; border-radius: 5px; margin-bottom: 5px; padding: 0 10px; } .textarea{ width:450px; height: 100px; background: #fff; border: 1px solid #CCCCCC; border-radius: 5px; margin-bottom: 5px; padding: 0 10px; } </style> <style> body{ background-color:#fff; } #chatFormDiv { width:300px; text-align:center; padding:5px; } #chatHeader { color:#6d6d6d; font-size:18px; font-weight:bold; float: center; } #prechat_submit { font-weight:bold; float: center; } label { width:150px; font-weight:bold; } input[type=text], textarea { height: 50px; width:400px; background: #fff; border: 1px solid #CCCCCC; border-radius: 5px; margin-bottom: 5px; padding: 0 10px; } input[type=email] { height: 30px; width:280px; background: #fff; border: 1px solid #CCCCCC; border-radius: 3px; margin-bottom: 5px; padding: 0 10px; } input[type=tel] { height: 30px; width:280px; background: #fff; border: 1px solid #CCCCCC; border-radius: 3px; margin-bottom: 5px; padding: 0 10px; } input[type=text] { height: 30px; } textarea{ height:140px; padding-top: 10px; padding-bottom: 10px; } .chatStatusDiv { display:none; } </style> </form> </div> </apex:page>
- RedmossSubC4i
- May 11, 2016
- Like
- 0
Bulkification Issue with Apex Program for Opportunity Tracking
I've written an apex program to capture opportunity metrics via timestamps. The goal is to calculate how long it takes a sales rep to reach certain opportunity amount thresholds and how long it takes them to get their first win and first lost opportunity. The amount thresholds are cumulative across multiple opportunities owned by each rep. The program seems to be working when activating the opportunity trigger for one opportunity/owner at a time, but jumbles the data when mass updating many opportunities owned by many users. All of the results are being store in a custom object called Sales Rep Ramp.
Any help at least identifying what the issue is will be greatly appreciated. Still working on it, but it may be during all the sorting, the nested loops, or the static collections in the class or something else. Thank you in advance!
Apex class code is below:
Any help at least identifying what the issue is will be greatly appreciated. Still working on it, but it may be during all the sorting, the nested loops, or the static collections in the class or something else. Thank you in advance!
Apex class code is below:
public class SalesRepRamp { //Recursive Control - Before Trigger public static boolean hasAlreadyRunBefore = false; public static boolean hasAlreadyRunMethodBefore(){ return hasAlreadyRunBefore; } public static void setAlreadyRunMethodBefore(){ hasAlreadyRunBefore = true; } //Recursive Control - After Trigger public static boolean hasAlreadyRunAfter = false; public static boolean hasAlreadyRunMethodAfter(){ return hasAlreadyRunAfter; } public static void setAlreadyRunMethodAfter(){ hasAlreadyRunAfter = true; } //Reference static Set<Id> userIds = new Set<Id>(); static Map<Id,User> userMap = new Map<Id,User>(OpportunitySharedResources.userMap); static Map<Id,Opportunity> oppMap = new Map<Id,Opportunity>(); static Map<String,Sales_Rep_Ramp__c> salesRampMap = new Map<String,Sales_Rep_Ramp__c>(); //Map<UserId+SalesRepRampType,Sales_Rep_Ramp__c> //First Win or First Loss static Map<Id,Map<Date,Opportunity>> userFirstCloseOppMap = new Map<Id,Map<Date,Opportunity>>(); //Map<UserId,Map<CloseDate,Opportunity>> static Map<Id,List<Date>> userFirstCloseDateMap = new Map<Id,List<Date>>(); //Map<UserId,List<CloseDate>> //Pipeline Tracking static Map<Id,Map<Datetime,Opportunity>> userPipelineOppMap = new Map<Id,Map<Datetime,Opportunity>>(); //Map<UserId,Map<Last_Software_Appliance_Modified_Date__c, Opportunity>> static Map<Id,List<Datetime>> userPipelineDatetimeMap = new Map<Id,List<Datetime>>(); //Map<UserId,List<Last_Software_Appliance_Modified_Date__c>> //Results static Map<String,Datetime> thresholdMap = new Map<String,Datetime>(); static Map<Id,Map<String,Datetime>> userResultsMap = new Map<Id,Map<String,Datetime>>(); //Map<UserId,Map<SalesRepRampType,Last_Software_Appliance_Modified_Date__c>> static List<Sales_Rep_Ramp__c> insertSRR = new List<Sales_Rep_Ramp__c>(); public static void entryMethod(List<Opportunity> newList, Map<Id,Opportunity> oldMap, String triggerType){ if(triggerType == 'Before Insert' || triggerType == 'Before Update'){ lastAmountTimestamp(newlist, oldMap, triggerType); adjustClosedLostDate(newList, oldMap, triggerType); } if(triggerType == 'After Insert' || triggerType == 'After Update'){ //Only process Opportunities owned by new sales reps for(Opportunity o: newList){ if(userMap.containsKey(o.OwnerId) && userMap.get(o.OwnerId).Sales_Rep_Ramp__c == true && userMap.get(o.OwnerId).Created_Date__c != null){ userIds.add(o.OwnerId); } } } if(userIds.size() > 0){ getSalesRepRamp(); getRelatedOpps(); processRelatedOpps(newList,oldMap); if(userResultsMap.size() > 0){ updateSalesRepRamp(); } } } public static void lastAmountTimestamp(List<Opportunity> newList, Map<Id,Opportunity> oldMap, String triggerType){ if(triggerType == 'Before Update'){ for(Opportunity o: newList){ Opportunity oldOpp = oldMap.get(o.Id); if(o.Total_Software_and_Appliance__c != oldOpp.Total_Software_and_Appliance__c){ o.Last_Software_Appliance_Modified_Date__c = datetime.now(); } } } } public static void adjustClosedLostDate(List<Opportunity> newList, Map<Id,Opportunity> oldMap, String triggerType){ if(triggerType == 'Before Update'){ for(Opportunity o:newList){ Opportunity oldOpp = oldMap.get(o.Id); if((o.StageName == 'Closed Lost' || o.StageName == 'Closed - Deferred') && oldOpp.StageName != o.StageName && oldOpp.StageName != 'Closed Lost' && oldOpp.StageName != 'Closed - Deferred'){ o.CloseDate = date.today(); } } } } public static void getSalesRepRamp(){ List<Sales_Rep_Ramp__c> salesRampList = new List<Sales_Rep_Ramp__c>([SELECT Id, User__c, Type__c, Timestamp__c FROM Sales_Rep_Ramp__c WHERE User__c IN: userIds]); for(Sales_Rep_Ramp__c srr: salesRampList){ String uniqueKey = srr.User__c+srr.Type__c; salesRampMap.put(uniqueKey,srr); } } public static void getRelatedOpps(){ List<Opportunity> oppList = new List<Opportunity>([ SELECT Id, Name, OwnerId, CloseDate, CreatedDate, StageName, Total_Software_and_Appliance__c, Last_Software_Appliance_Modified_Date__c, Total_Amount__c, IsClosed, IsWon FROM Opportunity WHERE OwnerId IN: userIds AND Total_Software_and_Appliance__c > 0 AND Last_Software_Appliance_Modified_Date__c != null ]); oppMap.putAll(oppList); } public static void processRelatedOpps(List<Opportunity> newList, Map<Id,Opportunity> oldMap){ //Reset thresholdMap thresholdMap.clear(); for(Opportunity o: oppMap.values()){ /*First Win or First Loss - Setup*/ if(o.IsClosed == true){ //Collect Opp by Rep and Date (Close Date) if(userFirstCloseOppMap.containsKey(o.OwnerId)){ userFirstCloseOppMap.get(o.OwnerId).put(o.CloseDate,o); } else{ Map<Date,Opportunity> OppDateMap = new Map<Date,Opportunity>(); OppDateMap.put(o.CloseDate,o); userFirstCloseOppMap.put(o.OwnerId,OppDateMap); } //Collect opps by Rep into Date lists if(userFirstCloseDateMap.containsKey(o.OwnerId)){ userFirstCloseDateMap.get(o.OwnerId).add(o.CloseDate); } else{ List<Date> OppDateList = new List<Date>(); OppDateList.add(o.CloseDate); userFirstCloseDateMap.put(o.OwnerId,OppDateList); } } /*Pipeline - Setup*/ if(o.IsClosed == false || (o.IsClosed == true && o.IsWon == true)){ //Collect Opps by Rep and Datetime (Last Software Appliance Modified Date) if(userPipelineOppMap.containsKey(o.OwnerId)){ userPipelineOppMap.get(o.OwnerId).put(o.Last_Software_Appliance_Modified_Date__c,o); } else{ Map<Datetime,Opportunity> OppDatetimeMap = new Map<Datetime,Opportunity>(); OppDatetimeMap.put(o.Last_Software_Appliance_Modified_Date__c,o); userPipelineOppMap.put(o.OwnerId,OppDatetimeMap); } //Collect Opps by Rep into Datetime lists if(userPipelineDatetimeMap.containsKey(o.OwnerId)){ userPipelineDatetimeMap.get(o.OwnerId).add(o.Last_Software_Appliance_Modified_Date__c); } else{ List<Datetime> OppDatetimeList = new List<Datetime>(); OppDatetimeList.add(o.Last_Software_Appliance_Modified_Date__c); userPipelineDatetimeMap.put(o.OwnerId,OppDatetimeList); } } } /*First Win or First Loss - Process*/ for(Id repId: userFirstCloseDateMap.keySet()){ List<Date> OppDateList = userFirstCloseDateMap.get(repId); //Sort Close Date by oldest first OppDateList.sort(); //Find Opps with First Win or First Loss for(Date closeDate: OppDateList){ Opportunity o = userFirstCloseOppMap.get(repId).get(closeDate); //First Win if(o.IsClosed == true && o.IsWon == true && !thresholdMap.containsKey('First Win')){ Time myTime = Time.newInstance(5,0,0,0); Datetime timestamp = Datetime.newInstance(closeDate,myTime); setupUserResultsMap(repId, 'First Win', timestamp); } //First Loss if(o.IsClosed == true && o.IsWon == false && !thresholdMap.containsKey('First Loss')){ Time myTime = Time.newInstance(5,0,0,0); Datetime timestamp = Datetime.newInstance(closeDate,myTime); setupUserResultsMap(repId, 'First Loss', timestamp); } } } /*Pipeline - Process*/ for(Id repId: userPipelineDatetimeMap.keySet()){ List<Datetime> OppDatetimeList = userPipelineDatetimeMap.get(repId); //Sort Last Software/Appliance Modified Date by oldest first OppDatetimeList.sort(); //SUM Opp Total Software and Appliance in order from oldest to newest Decimal totalSWandApp = 0; for(Datetime timestamp: OppDatetimeList){ Opportunity o = userPipelineOppMap.get(repId).get(timestamp); if(totalSWandApp > 0){ totalSWandApp = totalSWandApp + o.Total_Software_and_Appliance__c; } else{ totalSWandApp = o.Total_Software_and_Appliance__c; } //Check current total and verify Amount thresholds //50k Pipeline if(totalSWandApp >= 50000 && !thresholdMap.containsKey('50k Pipeline')){ setupUserResultsMap(repId, '50k Pipeline', timestamp); } //100k Pipeline if(totalSWandApp >= 100000 && !thresholdMap.containsKey('100k Pipeline')){ setupUserResultsMap(repId, '100k Pipeline', timestamp); } //150k Pipeline if(totalSWandApp >= 150000 && !thresholdMap.containsKey('150k Pipeline')){ setupUserResultsMap(repId, '150k Pipeline', timestamp); } //200k Pipeline if(totalSWandApp >= 200000 && !thresholdMap.containsKey('200k Pipeline')){ setupUserResultsMap(repId, '200k Pipeline', timestamp); } //250k Pipeline if(totalSWandApp >= 250000 && !thresholdMap.containsKey('250k Pipeline')){ setupUserResultsMap(repId, '250k Pipeline', timestamp); } //400k Pipeline if(totalSWandApp >= 400000 && !thresholdMap.containsKey('400k Pipeline')){ setupUserResultsMap(repId, '400k Pipeline', timestamp); } //500k Pipeline if(totalSWandApp >= 500000 && !thresholdMap.containsKey('500k Pipeline')){ setupUserResultsMap(repId, '500k Pipeline', timestamp); } } } } public static void setupUserResultsMap(Id repId, String Type, Datetime timestamp){ if(userResultsMap.containsKey(repId) && !userResultsMap.get(repId).containsKey(Type)){ userResultsMap.get(repId).put(Type,timestamp); } else{ thresholdMap.put(Type,timestamp); userResultsMap.put(repId,thresholdMap); } } public static Sales_Rep_Ramp__c setupSalesRepRamp(Id repId, String Type){ Sales_Rep_Ramp__c srr = new Sales_Rep_Ramp__c( User__c = repId, Type__c = Type, Timestamp__c = userResultsMap.get(repId).get(Type) ); return srr; } public static void processSalesRepRamp(Id userId, String Type){ String uniqueKey = userId+Type; //Check for existing and only insert new Sales Rep Ramp record if(!salesRampMap.containsKey(uniqueKey) && userResultsMap.containsKey(userId) && userResultsMap.get(userId).containsKey(Type)){ Sales_Rep_Ramp__c srr = setupSalesRepRamp(userId, Type); insertSRR.add(srr); } } public static void updateSalesRepRamp(){ //Refresh insert list insertSRR.clear(); //Setup for each threshold for(Id repId: userResultsMap.keySet()){ //50k Pipeline processSalesRepRamp(repId,'50k Pipeline'); //100k Pipeline processSalesRepRamp(repId,'100k Pipeline'); //150k Pipeline processSalesRepRamp(repId,'150k Pipeline'); //200k Pipeline processSalesRepRamp(repId,'200k Pipeline'); //250k Pipeline processSalesRepRamp(repId,'250k Pipeline'); //400k Pipeline processSalesRepRamp(repId,'400k Pipeline'); //500k Pipeline processSalesRepRamp(repId,'500k Pipeline'); //First Win processSalesRepRamp(repId,'First Win'); //First Loss processSalesRepRamp(repId,'First Loss'); } if(insertSRR.size() > 0){ insert insertSRR; } } }
- RedmossSubC4i
- August 13, 2015
- Like
- 0
AggregateResult - System.ListException: Row with null Id at index: 0
One of my aggregate result SOQL queries is getting the error, System.ListException: Row with null Id at index: 0. If I'm correct this tells me that the query came up with no results. What's confusing is that I have another aggregate result that is super similar that works just fine. Can anyone please help explain how to fix this? Thanks in advance!
Problem code:
//Aggreate SOQL to get next scheduled task
Map<Id,AggregateResult> LDRnextTaskSummary = new Map<Id,AggregateResult>([SELECT WhoId LeadId, MIN(CreatedDate) NextDate FROM Task WHERE (WhoId IN: leadIds) AND (Owner.Profile.Name LIKE '%LDR%') AND (Status = 'Not Started' OR Status = 'In Progress') AND (ActivityDate >= TODAY) GROUP BY WhoId]);
Similar SOQL That Works:
//Aggregate SOQL to get First and Last Tasks
Map<Id,AggregateResult> LDRpastTaskSummary = new Map<Id,AggregateResult>([SELECT WhoId LeadId, MIN(CreatedDate) FirstDate, MAX(CreatedDate) LastDate FROM Task WHERE (WhoId IN :leadIds) AND (Owner.Profile.Name LIKE '%LDR%') AND (Status = 'Completed') GROUP BY WhoId]);
Was testing and this also got the same error:
Map<Id,AggregateResult> LDRnextTaskSummary = new Map<Id,AggregateResult>([SELECT WhoId LeadId, MIN(CreatedDate) NextDate FROM Task WHERE (WhoId IN: leadIds) AND (Owner.Profile.Name LIKE '%LDR%') GROUP BY WhoId]);
Problem code:
//Aggreate SOQL to get next scheduled task
Map<Id,AggregateResult> LDRnextTaskSummary = new Map<Id,AggregateResult>([SELECT WhoId LeadId, MIN(CreatedDate) NextDate FROM Task WHERE (WhoId IN: leadIds) AND (Owner.Profile.Name LIKE '%LDR%') AND (Status = 'Not Started' OR Status = 'In Progress') AND (ActivityDate >= TODAY) GROUP BY WhoId]);
Similar SOQL That Works:
//Aggregate SOQL to get First and Last Tasks
Map<Id,AggregateResult> LDRpastTaskSummary = new Map<Id,AggregateResult>([SELECT WhoId LeadId, MIN(CreatedDate) FirstDate, MAX(CreatedDate) LastDate FROM Task WHERE (WhoId IN :leadIds) AND (Owner.Profile.Name LIKE '%LDR%') AND (Status = 'Completed') GROUP BY WhoId]);
Was testing and this also got the same error:
Map<Id,AggregateResult> LDRnextTaskSummary = new Map<Id,AggregateResult>([SELECT WhoId LeadId, MIN(CreatedDate) NextDate FROM Task WHERE (WhoId IN: leadIds) AND (Owner.Profile.Name LIKE '%LDR%') GROUP BY WhoId]);
- RedmossSubC4i
- April 25, 2014
- Like
- 0
Apex Trigger on Parent update child
I have the below trigger, to update a child if a field on the parent is a specific value.
I am getting the below error
Error: Compile Error: Invalid field Status__c for SObject ADR__c at line 28 column 1
trigger ADRParentUpdateChild on ADR__c ( after update, after insert) {
List<ADR__c > ParentRecords= [Select id, Next_Step__c, (Select ADR__c, Status__c FROM ADRUser__r) from ADR__c WHERE id IN :Trigger.newMap.keySet()];
for(ADR__c parent: ParentRecords){
if(parent.Next_Step__c == 'Submit'){
parent.Status__c = 'Submit';
}
}
}
I am getting the below error
Error: Compile Error: Invalid field Status__c for SObject ADR__c at line 28 column 1
trigger ADRParentUpdateChild on ADR__c ( after update, after insert) {
List<ADR__c > ParentRecords= [Select id, Next_Step__c, (Select ADR__c, Status__c FROM ADRUser__r) from ADR__c WHERE id IN :Trigger.newMap.keySet()];
for(ADR__c parent: ParentRecords){
if(parent.Next_Step__c == 'Submit'){
parent.Status__c = 'Submit';
}
}
}
- Bryan Revelant 7
- April 25, 2014
- Like
- 0