You need to sign in to do that
Don't have an account?
sfdc dev 2264
converting a trigger /class to a batch class help needed
Hi,
I am having a trigger referred in a class which works fine , but fails in some cases , I would like to convert that class to a batch class and use it in my trigger
Kindly help me pls
Kindly help me pls
Thanks in advance
I am having a trigger referred in a class which works fine , but fails in some cases , I would like to convert that class to a batch class and use it in my trigger
Kindly help me pls
MY TRIGGER CODE: trigger ContractTrigger on Contract__c(After Update,After Insert) { try{ Trigger_Status__c ts = Trigger_Status__c.getValues('ContractTrigger'); if(ts.Active__c){ if(Trigger.isUpdate){ TravelFundHandler.ValidateContractTermination(Trigger.New,Trigger.oldMap); ContractTriggerHandler.ContractfieldsupdateonAccount(Trigger.New, Trigger.oldMap); ContractTriggerHandler.contractcommencementdate(Trigger.New, Trigger.oldMap); ContractTriggerHandler.contractexpirydate(Trigger.New, Trigger.oldMap); } if(Trigger.isInsert){ TravelFundHandler.travelFundMethod(Trigger.New,Trigger.oldMap); } if(Trigger.isUpdate && TravelFundHandler.firstRun){ TravelFundHandler.firstRun=false; TravelFundHandler.SignOnMethod(Trigger.New,Trigger.oldMap); } } }catch(Exception e){ System.debug('Error Occured From Contract Trigger: '+e.getMessage()); } }
MY CLASS CODE : public class triggerhandler{ public static void ContractfieldsupdateonAccount(List<Contract__c> newList, Map<Id, Contract__c> oldMap){ Map<Id, Date> accMaptomaxcontractenddate = new Map<Id, Date>(); Set<Id> accIds = new Set<Id>(); List<Account> updateAcccontinfo = new List<Account>(); Date d; //Collecting the Account information from the Contract where the Status is 'Signed by Customer' for(Contract__c contr: newList){ if(contr.Status__c == 'Signed by Customer') accIds.add(contr.Account__c); System.debug('Account Ids: '+accIds); AggregateResult[] groupedResults = [SELECT Id,Account__c,MAX(Contract_End_Date__c) FROM Contract__c where Account__c IN :accIds and Contracted__c = TRUE group by Account__c,Id ORDER BY MAX(Contract_End_Date__c) DESC NULLS LAST LIMIT 1]; // To get the Maximum contract end date of the Active (End Date > Today > Start date) Contract for(AggregateResult ar :groupedResults){ d = (Date)ar.get('expr0'); System.debug('Maximum Contract End date: '+d); accMaptomaxcontractenddate.put((Id)ar.get('Account__c'),(Date)ar.get('expr0')); System.debug('Accmap for contracted Maximum Contract End date: : '+accMaptomaxcontractenddate); } // To get the Account information associated to the Maximum contract end date of the Active (End Date > Today > Start date) Contract Map<id,Account> accMaptocontractfields = new Map<id,Account>(); accMaptocontractfields = new Map<Id, Account>([SELECT Id, Contract_Start_Date__c, Contract_End_Date__c, Contract_Commencement_Date__c, Contract_Expiry_Date__c, No_of_Contracts__c,Qantas_Annual_Expenditure__c,Contract_Term_in_Months__c,Domestic_Annual_Share__c, International_Annual_Share__c, Unit_Spend_per_term__c, Contracted__c,(SELECT Id, Contract_Start_Date__c, Contracted__c, Contract_End_Date__c, Domestic_Annual_Share__c, International_Annual_Share__c, Type__c,Qantas_Annual_Expenditure__c,Contract_Term_in_Months__c, Unit_Spend_per_term__c FROM Contracts__r where (Contract_End_Date__c = :d AND Contracted__c = TRUE) ORDER BY CreatedDate DESC NULLS LAST limit 1 ) FROM Account where Id IN : accIds ]); System.debug('Account map for contract end date:'+accMaptocontractfields); // To get the Contract associated to the Account from the map mentioned above Account acco = accMaptocontractfields.get(contr.Account__c); Date startDate,endDate; Decimal domShare, intShare, unitspend,qantasannual,term; String type; Boolean contracted = false; for(Contract__c contr1 : acco.Contracts__r){ if(startDate == null && endDate == null){ startDate = contr1.Contract_Start_Date__c; endDate = contr1.Contract_End_Date__c; domShare = contr1.Domestic_Annual_Share__c; intShare = contr1.International_Annual_Share__c; unitspend = contr1.Unit_Spend_per_term__c; qantasannual = contr1.Qantas_Annual_Expenditure__c; type = contr1.Type__c; term = contr1.Contract_Term_in_Months__c; contracted = contr1.Contracted__c; }else if(startDate.daysBetween(endDate) < contr1.Contract_Start_Date__c.daysBetween(contr1.Contract_End_Date__c)){ startDate = contr1.Contract_Start_Date__c; endDate = contr1.Contract_End_Date__c; domShare = contr1.Domestic_Annual_Share__c; intShare = contr1.International_Annual_Share__c; unitspend = contr1.Unit_Spend_per_term__c; qantasannual = contr1.Qantas_Annual_Expenditure__c; type = contr1.Type__c; term = contr1.Contract_Term_in_Months__c; contracted = contr1.Contracted__c; } } //Setting the Highest end date Active Contract information on Account acco.Contract_Start_Date__c = startDate; acco.Contract_End_Date__c = endDate; acco.Domestic_Annual_Share__c = domShare; acco.International_Annual_Share__c = intShare; acco.Type__c = type ; acco.Qantas_Annual_Expenditure__c = qantasannual; acco.Contract_Term_in_Months__c = term; acco.Unit_Spend_per_term__c = unitspend; acco.Contracted__c = contracted; System.debug('Account Contract Start Date: '+ acco.Contract_Start_Date__c ); System.debug('Account Contract End date: '+acco.Contract_End_Date__c); System.debug('Contract Term in Months: '+acco.Contract_Term_in_Months__c); System.debug('Units spend per term: '+acco.Unit_Spend_per_term__c); System.debug('Account Contract Category: '+acco.Type__c); System.debug('Forecast QF Spend: ' +acco.Qantas_Annual_Expenditure__c); System.debug('Forecast QF Share (Dom) [System]: '+acco.Domestic_Annual_Share__c); System.debug('Forecast QF Share (Int) [System]: '+acco.International_Annual_Share__c); System.debug('Contracted:'+acco.Contracted__c); updateAcccontinfo.add(acco); } try{ Database.SaveResult[] srlist = Database.Update(updateAcccontinfo, false); for (Database.SaveResult sr : srlist){ if (!sr.isSuccess()) { // Updation failed due to duplicate detected for(Database.Error duplicateError : sr.getErrors()){ Datacloud.DuplicateResult duplicateResult = ((Database.DuplicateError)duplicateError).getDuplicateResult(); System.debug('Duplicate records have been detected by ' + duplicateResult.getDuplicateRule()); System.debug(duplicateResult.getErrorMessage()); } // If the duplicate rule is an alert rule, we can try to bypass it Database.DMLOptions dml = new Database.DMLOptions(); dml.DuplicateRuleHeader.AllowSave = true; Database.SaveResult[] sr2list = Database.Update(updateAcccontinfo, dml); for (Database.SaveResult sr2 : sr2list){ if (sr2.isSuccess()) { System.debug('Duplicate account has been updated in Salesforce!'); } } } } } catch(Exception e){ System.debug(' Exception Occured: '+e.getMessage()); } } /* This method is being used to populate the Account fields "Contract Commencement Date" of the Minimum contract start date with the status "Signed by Customer" */ public static void contractcommencementdate(List<Contract__c> newList, Map<Id, Contract__c> oldMap){ Map<Id, Date> accMaptomincontractstartdate = new Map<Id, Date>(); Set<Id> accoIds = new Set<Id>(); List<Account> updateAcccommdate = new List<Account>(); Date d; Date commencementDate; //Collecting the Account information from the Contract where the Status is 'Signed by Customer' for(Contract__c contr: newList){ if(contr.Status__c == 'Signed by Customer' ) accoIds.add(contr.Account__c); // To get the Minimum contract start date of the ""Signed by Customer" Contract AggregateResult[] groupedResults = [SELECT Id,Account__c, MIN(Contract_Start_Date__c) FROM Contract__c where Account__c IN :accoIds and Status__c = 'Signed by Customer' group by Account__c,Id ORDER BY MIN(Contract_Start_Date__c) ASC NULLS LAST LIMIT 1]; for(AggregateResult ar : groupedResults){ accMaptomincontractstartdate.put((Id)ar.get('Account__c'),(Date)ar.get('expr0')); System.debug('Accmap for Minimum Contract Start date: : '+accMaptomincontractstartdate); d=(Date)ar.get('expr0'); System.debug('Least Commencement Date: '+d); } // To get the Account information associated to the Minimum contract start date of the "Signed by Customer" Contract Map<id,Account> accMaptocommencementdate = new Map<id,Account>(); accMaptocommencementdate = new Map<Id, Account>([SELECT Id, Contract_Start_Date__c, Contract_End_Date__c, Contract_Commencement_Date__c, Contract_Expiry_Date__c, No_of_Contracts__c,Qantas_Annual_Expenditure__c,Contract_Term_in_Months__c,Domestic_Annual_Share__c, International_Annual_Share__c, Unit_Spend_per_term__c, Contracted__c,(SELECT Id, Contract_Start_Date__c, Contracted__c, Contract_End_Date__c, Domestic_Annual_Share__c, International_Annual_Share__c, Type__c,Qantas_Annual_Expenditure__c,Contract_Term_in_Months__c, Unit_Spend_per_term__c FROM Contracts__r where (Contract_Start_Date__c = :d and Status__c = 'Signed by Customer')limit 1 ) FROM Account where Id IN : accoIds]); // To get the Contract associated to the Account from the map mentioned above Account accou = accMaptocommencementdate.get(contr.Account__c); for(Contract__c contr2 : accou.Contracts__r){ commencementDate = contr2.Contract_Start_Date__c; } //Setting the Minimum Contract start date to the Account Contract commencement date accou.Contract_Commencement_Date__c = commencementDate; System.debug('Account Contract Commenecement Date:'+accou.Contract_Commencement_Date__c); updateAcccommdate.add(accou); } try{ Database.SaveResult[] srlist = Database.Update(updateAcccommdate, false); for (Database.SaveResult sr : srlist){ if (!sr.isSuccess()) { // Updation failed due to duplicate detected for(Database.Error duplicateError : sr.getErrors()){ Datacloud.DuplicateResult duplicateResult = ((Database.DuplicateError)duplicateError).getDuplicateResult(); System.debug('Duplicate records have been detected by ' + duplicateResult.getDuplicateRule()); System.debug(duplicateResult.getErrorMessage()); } // If the duplicate rule is an alert rule, we can try to bypass it Database.DMLOptions dml = new Database.DMLOptions(); dml.DuplicateRuleHeader.AllowSave = true; Database.SaveResult[] sr2list = Database.Update(updateAcccommdate, dml); for (Database.SaveResult sr2 : sr2list){ if (sr2.isSuccess()) { System.debug('Duplicate account has been updated in Salesforce!'); } } } } } catch(Exception e){ System.debug(' Exception Occured: '+e.getMessage()); } } public static void contractexpirydate(List<Contract__c> newList, Map<Id, Contract__c> oldMap){ Map<Id, Date> accMaptomaxcontractexpdate = new Map<Id, Date>(); Set<Id> accIds = new Set<Id>(); List<Account> updateAccexpdate = new List<Account>(); Date d; Date expiryDate; //Collecting the Account information from the Contract where the Status is 'Signed by Customer' for(Contract__c contr: newList){ if(contr.Status__c == 'Signed by Customer' ) accIds.add(contr.Account__c); // To get the Maximum contract end date of the ""Signed by Customer" Contract AggregateResult[] groupedResults = [SELECT Id,Account__c, MAX(Contract_End_Date__c) FROM Contract__c where Account__c IN :accIds and Status__c = 'Signed by Customer' group by Account__c,Id Order by MAX(Contract_End_Date__c) DESC NULLS LAST LIMIT 1]; for(AggregateResult ar : groupedResults){ accMaptomaxcontractexpdate.put((Id)ar.get('Account__c'),(Date)ar.get('expr0')); System.debug('Accmap for Maximum Contract End date: : '+accMaptomaxcontractexpdate); d=(Date)ar.get('expr0'); System.debug('Highest Expiry Date: '+d); } // To get the Account information associated to the Maximum contract end date of the ""Signed by Customer" Contract Map<id,Account> accMaptoExpirydate = new Map<id,Account>(); accMaptoExpirydate = new Map<Id, Account>([SELECT Id, Contract_Start_Date__c, Contract_End_Date__c, Contract_Commencement_Date__c, Contract_Expiry_Date__c, No_of_Contracts__c,Qantas_Annual_Expenditure__c,Contract_Term_in_Months__c,Domestic_Annual_Share__c, International_Annual_Share__c, Unit_Spend_per_term__c, Contracted__c, Account_Contract_No__c,(SELECT Id, Contract_Start_Date__c, Contracted__c, Contract_End_Date__c, Domestic_Annual_Share__c, International_Annual_Share__c, Type__c,Qantas_Annual_Expenditure__c,Contract_Term_in_Months__c, Unit_Spend_per_term__c,Contract_Number__c FROM Contracts__r where (Contract_End_Date__c = :d and Status__c = 'Signed by Customer')ORDER BY CreatedDate DESC NULLS LAST limit 1) FROM Account where Id IN : accIds ]); // To get the Contract associated to the Account from the map mentioned above Account accoun = accMaptoExpirydate.get(contr.Account__c); String Accconno; for(Contract__c contr2 : accoun.Contracts__r){ Accconno = contr2.Contract_Number__c; expiryDate = contr2.Contract_End_Date__c; } //Setting the Maximum Contract end date to the Account Contract commencement date accoun.Contract_Expiry_Date__c = expiryDate; accoun.Account_Contract_No__c = Accconno; System.debug('Account Contract Expiry Date:'+ accoun.Contract_Expiry_Date__c); System.debug('Account Contract No: '+ accoun.Account_Contract_No__c); updateAccexpdate.add(accoun); } try{ Database.SaveResult[] srlist = Database.Update(updateAccexpdate, false); for (Database.SaveResult sr : srlist){ if (!sr.isSuccess()) { // Updation failed due to duplicate detected for(Database.Error duplicateError : sr.getErrors()){ Datacloud.DuplicateResult duplicateResult = ((Database.DuplicateError)duplicateError).getDuplicateResult(); System.debug('Duplicate records have been detected by ' + duplicateResult.getDuplicateRule()); System.debug(duplicateResult.getErrorMessage()); } Database.DMLOptions dml = new Database.DMLOptions(); dml.DuplicateRuleHeader.AllowSave = true; Database.SaveResult[] sr2list = Database.Update(updateAccexpdate, dml); for (Database.SaveResult sr2 : sr2list){ if (sr2.isSuccess()) { System.debug('Duplicate account has been updated in Salesforce!'); } } } } } catch(Exception e){ System.debug(' Exception Occured: '+e.getMessage()); } } }
Kindly help me pls
Thanks in advance
Batch class with only ContractfieldsupdateonAccount method:
Contract trigger (I only included relevant code, so you will need to add the rest of your code):
I did not look very closely at your logic in the three methods, so you need to check that all things are working correctly.
Remember that there are limits to how many queries and DML transactions you can perform in each batch (https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex_gov_limits.htm).
All Answers
If you are going to call a batch class from a trigger, you have to be very careful not to go over the limit of running 5 batch jobs at a time.
Here is the documentation for implementing the Database.Batchable interface, so you can look through and get an idea for how to convert your code to a batch class: https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex_batch_interface.htm
Here are some important points:
- Use the start method to collect all the records you want to process. These records will be divided into batches and passed into the execute method.
- Use the execute method to process each batch of records.
- Use the finish method to execute any logic that you want to perform after all the records have been processed.
Based on your existing code, it looks like you will want to put your three trigger handler methods in the batch class and call the three of them in the batch execute method. You will then call the batch class from your trigger using the Database.executeBatch method.Here is an article that gives a good template for executing Batch Apex from a trigger: http://sfdcsrini.blogspot.com/2014/06/how-to-execute-batch-apex-using-apex.html
Here is another article listing Batch Apex best practices: http://sfdcrocket.blogspot.com/2013/12/batch-apex-best-practices.html
I hope this helps!
Batch class with only ContractfieldsupdateonAccount method:
Contract trigger (I only included relevant code, so you will need to add the rest of your code):
I did not look very closely at your logic in the three methods, so you need to check that all things are working correctly.
Remember that there are limits to how many queries and DML transactions you can perform in each batch (https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex_gov_limits.htm).
what does the lineno 24 indicate
that method logic has to come under execute method right ?
public void execute(Database.BatchableContext bc, List<Contract__c> scope) {
public void execute(Database.BatchableContext bc, List<Contract__c> scope) {
//complete code for this method has to be put here right
}
}
Hi sfdc,
Yes, u r right according to "Charisse" logic, Line no.24 indicate that the void function are called under execute method to make code more readable.
Thanks
Rajat Maheshwari
rajatzmaheshwari@gmail.com
Is my above logic inside my execute method right ?
public void execute(){
public method 1{
// logic in method 1 from main class
}
public method 2{
//logic in method 2 from main class
}
public method 3{
//logic in method 3 from main class
}
}
and that one query can alone be there in start method right , Please confirm
Yes, you will use only the one query in the the start method.
just one small confirmation at end
In the trigger i just have to call my batch class thats it right , i mean the above trigger mentioned by you is all it requires right , or should i call the batchclass.method name there also ?
Error: Compile Error: Method does not exist or incorrect signature: Database.QueryLocator(List<Contract__c>) at line 10 column 16
global Database.QueryLocator start(Database.BatchableContext bc) {
// Query all Contract records that were in Trigger.new
return Database.QueryLocator([SELECT Status__c, Account__c FROM Contract__c WHERE Id IN :contractIdSet]);
}
To answer your question from before, you do not need to call the batchclass.methodname. If you have more questions about how the batch class works, I recommend checking the Batch Apex documentation (https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex_batch_interface.htm).