You need to sign in to do that
Don't have an account?

Error when using @Future
public class WorkflowTickler { public static void TickleOpportunitySalesGoal(Opportunity_Sales_Goal__c[] opportunitySalesGoalArr) { DateTime currentTime = System.Now(); for(Opportunity_Sales_Goal__c opportunitySalesGoal:opportunitySalesGoalArr) { opportunitySalesGoal.Last_Sync_To_Parent__c = currentTime; } update opportunitySalesGoalArr; }}
The trigger that calls the method is
trigger syncParentOpportunitySales on Sales_Goal__c (after update) { Opportunity_Sales_Goal__c[] lstOpportunitySalesGoal = [Select Id from Opportunity_Sales_Goal__c where Sales_Goal__c in :Trigger.newMap.keyset()]; WorkflowTickler.TickleOpportunitySalesGoal(lstOpportunitySalesGoal); }
I ran into Too many rows issue when trying to update rows. So I am trying to use the @future to resolve the issue.
But when I try to use @future in the method it would not work since I keep getting this error
Save error: Unsupported parameter type LIST:SOBJECT:Service__c e
The documentation says
Take a look at the following. Essentially, you need to use that set as a parameter for your method and then use it as a filter in a query using "in" and then generate a list of objects to update. I didn't test this, but it should be functional.
public class WorkflowTickler {
@future(callout = false)
public static void TickleOpportunitySalesGoal(Set<String> s) {
DateTime currentTime = System.Now();
List<Opportunity_Sales_Goal__c> opportunitySalesGoalToUpdate = new List<Opportunity_Sales_Goal__c>();
for(Opportunity_Sales_Goal__c opportunitySalesGoal : [select id, name, Last_Sync_To_Parent__c where id in: s]) {
opportunitySalesGoal.Last_Sync_To_Parent__c = currentTime;
opportunitySalesGoalToUpdate.add(opportunitySalesGoal);
}
update opportunitySalesGoalToUpdate;
}
}
All Answers
The docs also say :
"The parameters specified must be primitive dataypes, arrays of primitive datatypes, or collections of primitive datatypes"
So what you can do is pass your Trigger.newMap.keySet() to your @future method and do your query there.
-David
Hi David
Thanks for replying. But in teh @future method how would I receive the trigger.newmap.keyset()?
The trigger would pass it as
trigger syncParentOpportunitySales on Sales_Goal__c (after update) { WorkflowTickler.TickleOpportunitySalesGoal(Trigger.newMap.keyset()); }
But how would do the @future method recieve this without the list? or am I missing something very obvious
future (callout=true) public static void TickleOpportunitySalesGoal(List<Opportunity_Sales_Goal__c> OpportunitySalesGoalList) { } }
I don't know how else to call without List data type?
If I use list data type as a parameter in the @future, I still get the same error
Unsupported parameter type LIST:SOBJECT:Opportunity_Sales_Goal__c
In the @future method, you should set your parameter as a set of type String. So:
future (callout=true)
public static void TickleOpportunitySalesGoal(Set<String> s) {
// Then use s as a parameter for a query to get your SObjects
}
You can then use this set as a parameter for a query to get your SObjects and make the needed updates. If using newMap.keySet() doesn't work, try generating a set of type String with the IDs of the incoming records (not sure if newMap.KeySet() is an ID type).
Thanks Matt for replying.
But If I use set<string> and try to loop through the trigger.new I get a
Loop variable must be of type Id
error.The code is
Set<string> opportunitysalesids = new Set<string> (); for (Opportunity_Sales_Goal__c link:Trigger.newMap.keyset()){ opportunitysalesids.add(link.name); }
I have tried different variants of the above code but nothing works. Can you help?
Just copy the data in the Set<ID> to a Set<String> using a for loop.
Is there a way to copy values from a list to a set?
I have a list of objects in a list that needs to be copied to a set of strings so I can pass it to the @futuire method
Opportunity_Sales_Goal__c[] lstOpportunitySalesGoal = [Select Id from Opportunity_Sales_Goal__c where Sales_Goal__c in :Trigger.newMap.keyset()];
This is the list that needs to be copied to a set of string
Thanks
Yes. Do this:
Set<string> opportunitysalesids = new Set<string> ();
for (Opportunity_Sales_Goal__c osg : [Select Id from Opportunity_Sales_Goal__c where Sales_Goal__c in :Trigger.newMap.keyset()]) {
opportunitysalesids.add(osg.id);
}
// Call @future method with opportunitysalesids as a parameter
Thanks very much Matt. I am not getting any error now. But the receiving method with the @future call out access the values from an array like this.
public class WorkflowTickler {
@future(callout = true)
public static void TickleOpportunitySalesGoal(Opportunity_Sales_Goal__c[] opportunitySalesGoalArr) {
DateTime currentTime = System.Now();
for(Opportunity_Sales_Goal__c opportunitySalesGoal:opportunitySalesGoalArr) {
opportunitySalesGoal.Last_Sync_To_Parent__c = currentTime;
}
update opportunitySalesGoalArr;
}
}
But if I pass the value as 'Set' how can the values be passed to the array?
Thanks again for patiently answering my queries
I appreciate your help and time
Take a look at the following. Essentially, you need to use that set as a parameter for your method and then use it as a filter in a query using "in" and then generate a list of objects to update. I didn't test this, but it should be functional.
public class WorkflowTickler {
@future(callout = false)
public static void TickleOpportunitySalesGoal(Set<String> s) {
DateTime currentTime = System.Now();
List<Opportunity_Sales_Goal__c> opportunitySalesGoalToUpdate = new List<Opportunity_Sales_Goal__c>();
for(Opportunity_Sales_Goal__c opportunitySalesGoal : [select id, name, Last_Sync_To_Parent__c where id in: s]) {
opportunitySalesGoal.Last_Sync_To_Parent__c = currentTime;
opportunitySalesGoalToUpdate.add(opportunitySalesGoal);
}
update opportunitySalesGoalToUpdate;
}
}
You should, but in my experience, it works pretty quickly - within 30 seconds typically (this can vary based on Salesforce utilization at the time). Instead, check the following to see if the job completed successfully or if there were errors:
Setup > Administration Setup > Monitoring > Apex Jobs
-- Matt
Thanks Matt.
I could see the status on the apex jobs pretty quicly but if there are 3 jobs 2 is completed and 1 is failed. Is there a way to find out why it failed?
Also after the @future call the system is quite slow . Not sure if its my perception, but it has happened 3/4 times. I have to log out and then log back in then its normal . But whenever I access the object which calls the @future call, it seems to be slow.
Is there even a possibility that @future call can slow down the system?
THanks
You should try turning on the debug logs and then rerunning the process that calls your @future method and review the logs. How many records does this typically modify?
Since the @future method is asynchronous and runs from a trigger when the system has sufficient resources, you shouldn't be seeing any kind of performance change when accessing the objects modified in that method.
-- Matt
Hello Matt and others
I implemented @future method to be called from my trigger and it seemed to be working fine. But in production I keep getting the following error messages
Failed to invoke future method 'public static void TickleOpportunitySalesGoal(SET:String)'
This is the method thats defined as @future callout = true
I also get this error message from the trigger thats calling the method
syncParentOpportunitySales: execution of AfterUpdate
caused by: System.AsyncException: Future method cannot be called from a future method: TickleOpportunitySalesGoal(SET:String)
Can anyone shed more light on these errors
Pls.let me know if you need more info
Within TickleOpportunitySalesGoal are you calling another @future method? If so, this is what's causing your error as you can't call another @future method from your original @future call. If not, post your code and I'll take a look.
-- Matt
Thanks Matt for replying. I am not calling @future inside another @future. I am posting my code so you can see if am ding something incorrect
This is the first trigger
trigger syncOpportunitySalesGoal on Opportunity (after update) { Set<string> opportunitysalesids = new Set<string> (); for (Opportunity_Sales_Goal__c osg : [Select Id from Opportunity_Sales_Goal__c where Opportunity__c in :Trigger.newMap.keyset()]) { opportunitysalesids.add(osg.id); } WorkflowTickler.TickleOpportunitySalesGoal(opportunitysalesids); }
This is the second trigger
trigger syncParentOpportunitySales on Sales_Goal__c (after update) { Set<string> opportunitysalesids = new Set<string> (); for (Opportunity_Sales_Goal__c osg : [Select Id from Opportunity_Sales_Goal__c where Sales_Goal__c in :Trigger.newMap.keyset()]) { opportunitysalesids.add(osg.id); } WorkflowTickler.TickleOpportunitySalesGoal(opportunitysalesids); }
This is the class with the @future method
public class WorkflowTickler { @future(callout = true) public static void TickleOpportunitySalesGoal(Set<String> s) { DateTime currentTime = System.Now(); List<Opportunity_Sales_Goal__c> opportunitySalesGoalToUpdate = new List<Opportunity_Sales_Goal__c>(); for(Opportunity_Sales_Goal__c opportunitySalesGoal : [select id, name, Last_Sync_To_Parent__c from Opportunity_Sales_Goal__c where id in: s]) { opportunitySalesGoal.Last_Sync_To_Parent__c = currentTime; opportunitySalesGoalToUpdate.add(opportunitySalesGoal); } update opportunitySalesGoalToUpdate; } }
I didn't see anything overly wrong with your code. Do you have any workflows or triggers that run on Opportunity_Sales_Goal__c that update Opportunity or Sales_Goal__c? (Trying to figure out if your updates in the @future method result in updates that result in additional @future methods.)
-- Matt
Thanks Matt. I am looking at the workflows and validation rules right now. There are way too many.
But is there a way to rewrie this code without using @future and also avoid too many rows error?
Thanks
It would be best to just remove whatever logic is causing @future methods to call other @future methods. But, if you just want to make sure your trigger doesn't call another @future method after calling the initial @future method you could do something like the following:
1. Add a true/false field to your object called FutureMethodCalled - defaults to false
2. In your trigger query for this and ensure that it is marked as false, if so add that object id to the set of IDs to pass to the @future method
3. Set the field to true on the objects passed to the @future method within your calling trigger
This should stop future calls, but it will also only allow you to call your @future method once with the specified records so I think it is probably best to work through your workflow rules / other triggers and modify those that are causing the issue initially.