I am trying to write a VF page-controller for an approval process which should submit over 1500 records at the same time. The approval is 2 stage.. after initial submission, it should be approved by approver1 and after that records should go to approver2 for approval. Post that, process should end. Now the challenge is SF limits the number of emails in an execution to 1000 only. This is preventing me from using standard approval process (Setup-->Build-->Create-->Approval Process).
[There might be a work around but I don't know it. I have checked with Salesforce Support/Architecture and they confirmed that email limitation is preventing standard approval process to approve records , as the process is trying to send over 1000 emails at the same time. Plus I cannot turn off sending emails upon approval process launch].
So I have to go custom. The point where I am getting stuck is submitting all selected records for approval. I am using a component titled "CheckAllOrNone" for selecting all records.
As per below code, I have Year and Quarter filters to select a subset of records. When checkbox on the header is checked, all records are selected on the 1st page, but none on the second page [pagination]. I would like some assist on how to be able to select records on all pages and then submit them for approval, where I can lock the records using "FOR UPDATE" from edit. Plus any guidance on custom approval process would be helpful.
[NOTE : I have purposely removed some code from both page (to reduce size as it wasn't relevant to this discussion) and class (approval process, as it wasn't correct)]
VF Page ..
<apex:page controller="FormerAuthorController" sidebar="false" applyHtmlTag="true">
<apex:form >
<apex:pageMessages id="errors" />
<apex:pageBlock title="Former Author Payment!" mode="edit">
<table width="100%" border="0">
<tr>
<td width="200" valign="top">
<apex:pageBlock title="Parameters" mode="edit" id="criteria">
<script type="text/javascript">
function doSearch() {
searchServer(
document.getElementById("year").value,
document.getElementById("fiscal").options[document.getElementById("fiscal").selectedIndex].value
);
}
</script>
<apex:actionFunction name="searchServer" action="{!runSearch}" rerender="results,debug,errors">
<apex:param name="year" value="" />
<apex:param name="fiscal" value="" />
</apex:actionFunction>
<table cellpadding="2" cellspacing="2">
<tr>
<td style="font-weight:bold;">Fiscal-Quarter<br/>
<select id="fiscal" onchange="doSearch();">
<option value=""></option>
<apex:repeat value="{!fiscal}" var="quarter">
<option value="{!quarter}">{!quarter}</option>
</apex:repeat>
</select>
</td>
</tr>
<tr>
<td style="font-weight:bold;">Year<br/>
<input type="number" id="year" onkeyup="doSearch();"/>
</td>
</tr>
<!-- tr>
<td style="font-weight:bold;">Last Name<br/>
<input type="text" id="lastName" onkeyup="doSearch();"/>
</td>
</tr>
<tr>
<td style="font-weight:bold;">Account<br/>
<input type="text" id="accountName" onkeyup="doSearch();"/>
</td>
</tr -->
</table>
</apex:pageBlock>
</td>
<td valign="top">
<apex:pageBlock mode="edit" id="results">
<apex:pageBlockButtons >
<apex:commandButton value="Submit" action="{!submitForApproval}"/>
<apex:commandButton value="Cancel" action="{!Cancel}"/>
<apex:commandButton value="Approve" action="{!Approve}"/>
<apex:commandButton value="Reject" action="{!Reject}" />
</apex:pageBlockButtons>
<apex:pageBlockButtons >
<apex:commandButton value="First" action="{!Beginning}"/>
<apex:commandButton value="Previous" action="{!Previous}"/>
<apex:commandButton value="Next" action="{!Next}"/>
<apex:commandButton value="Last" action="{!End}" />
</apex:pageBlockButtons>
<apex:pageBlockTable value="{!monAct}" var="monRec">
<!-- apex:column >
<apex:facet name="header">
<apex:inputCheckbox>
<apex:actionSupport event="onClick" action="{!getSelected}" reRender="selection"/>
</apex:inputCheckbox>
</apex:facet>
<apex:inputCheckbox value="{!monRec.Checkbox__c}" id="checkdone">
<apex:actionSupport event="onClick" action="{!getSelected}" reRender="selection"/>
</apex:inputCheckbox>
</apex:column -->
<apex:column >
<apex:facet name="header">
<c:CheckAllorNone />
</apex:facet>
<apex:inputCheckbox value="{!monRec.Checkbox__c}"/>
</apex:column>
<apex:column >
<apex:facet name="header">
<apex:commandLink value="GID" action="{!toggleSort}" rerender="results,debug">
<apex:param name="sortField" value="GID__c" assignTo="{!sortField}"/>
</apex:commandLink>
</apex:facet>
<apex:outputField value="{!monRec.GID__c}"/>
</apex:column>
<apex:column >
<apex:facet name="header">
<apex:commandLink value="Month" action="{!toggleSort}" rerender="results,debug">
<apex:param name="sortField" value="Month_Text__c" assignTo="{!sortField}"/>
</apex:commandLink>
</apex:facet>
<apex:outputField value="{!monRec.Month_Text__c}"/>
</apex:column>
<apex:column >
<apex:facet name="header">
<apex:commandLink value="Quarter" action="{!toggleSort}" rerender="results,debug">
<apex:param name="sortField" value="Quarter__c" assignTo="{!sortField}"/>
</apex:commandLink>
</apex:facet>
<apex:outputField value="{!monRec.Quarter__c}"/>
</apex:column>
<apex:column >
<apex:facet name="header">
<apex:commandLink value="Year" action="{!toggleSort}" rerender="results,debug">
<apex:param name="sortField" value="year" assignTo="{!sortField}"/>
</apex:commandLink>
</apex:facet>
<apex:outputField value="{!monRec.Year__c}"/>
</apex:column>
</apex:pageBlockTable>
</apex:pageBlock>
</td>
</tr>
</table>
<apex:pageBlock title="Debug - SOQL" id="debug">
<apex:outputText value="{!debugSoql}" />
</apex:pageBlock>
</apex:pageBlock>
</apex:form>
Class:
public with sharing class FormerAuthorController {
public List<Monthly_Activity__c> monAct {get; set;}
private String soql{get; set;}
public Boolean checkAll{get; set;}
private String fiscalPeriod;
private String yearValue;
private static final String EDITORIAL_GROUP_NAME = 'Editorial';
private static final String FINANCIAL_GROUP_NAME = 'Finance';
private integer counter=0; //keeps track of the offset
private integer list_size=25; //sets the page size or number of rows
public integer total_size; //used to show user the total size of the list
//List<Monthly_Activity__c> selectedRecords = new List<Monthly_Activity__c>();
private static Id financeGroupId
{
get
{
if (financeGroupId == null)
{
financeGroupId = [SELECT Id FROM Group WHERE Name = :FINANCIAL_GROUP_NAME][0].Id;
}
return financeGroupId;
}
private set;
}
private static Id editorialGroupId
{
get
{
if (editorialGroupId == null)
{
editorialGroupId = [SELECT Id FROM Group WHERE Name = :EDITORIAL_GROUP_NAME][0].Id;
}
return editorialgroupId;
}
private set;
}
private static Id submitterProfileId
{
get
{
if (submitterProfileId == null)
{
submitterProfileId = [SELECT Id FROM Profile WHERE Name = 'Submitters'][0].Id;
}
return submitterProfileId;
}
private set;
}
private static Id financeProfileId
{
get
{
if (financeProfileId == null)
{
financeProfileId = [SELECT Id FROM Profile WHERE Name = 'Finance'][0].Id;
}
return financeProfileId;
}
private set;
}
private static Id editorialProfileId
{
get
{
if (editorialProfileId == null)
{
editorialProfileId = [SELECT Id FROM Profile WHERE Name = 'Editorial'][0].Id;
}
return editorialProfileId;
}
private set;
}
private static Id adminProfileId
{
get
{
if (adminProfileId == null)
{
adminProfileId = [SELECT Id FROM Profile WHERE Name = 'System Administrator'][0].Id;
}
return adminProfileId;
}
private set;
}
// the current sort direction. defaults to asc
public String sortDir {
get{
if(sortDir == null){
sortDir = 'asc';
}
return sortDir;
}
set;
}
// the current field to sort by. defaults to last name
public String sortField {
get{
if (sortField == null) {
sortField = 'Year__c';
} return sortField;
}
set;
}
// format the soql for display on the visualforce page
public String debugSoql {
get{
return soql + ' order by ' + sortField + ' ' + sortDir + ' limit '+list_size+' '+ 'offset '+counter;
}
set;
}
public FormerAuthorController(){
total_size = [SELECT count() FROM Monthly_Activity__c WHERE Contract_Status__c =:'Former Author'];
}
// toggles the sorting of query from asc<-->desc
public void toggleSort() {
// simply toggle the direction
sortDir = sortDir.equals('asc') ? 'desc' : 'asc';
// run the query again
runQuery();
}
// runs the actual query
public void runQuery() {
try {
//monAct = Database.query(soql + ' order by ' + sortField + ' ' + sortDir + ' '+' Limit '+list_size+' '+' OFFSET '+counter);
monAct = Database.query(debugSoql);
system.debug('RUN QUERY ' + monAct);
} catch (Exception e) {
ApexPages.addMessage(new ApexPages.Message(ApexPages.Severity.ERROR, 'Something Went Wrong. Contact System Adminstrator!'));
system.debug('Error Message ***'+ e.getMessage());
}
}
// runs the search with parameters passed via Javascript
public PageReference runSearch() {
system.debug('RUN SEARCH');
String fiscal = Apexpages.currentPage().getParameters().get('fiscal');
fiscalPeriod = fiscal;
String year = Apexpages.currentPage().getParameters().get('year');
yearValue = year;
system.debug('FISCAL '+ fiscal);
system.debug('YEAR '+ year);
soql = 'SELECT Id, Name, Month__c, Checkbox__c FROM Monthly_Activity__c WHERE Contract_Status__c = \'Former Author\'';
if (!year.equals(''))
soql += ' and Year__c ='+ year;
if (!fiscal.equals(''))
soql += ' and Quarter__c LIKE \'' +String.escapeSingleQuotes(fiscal)+'\'';
// run the query again
runQuery();
system.debug('RUN SEARCH QUERY');
return null;
}
// use apex describe to build the picklist values
public List<String> fiscal {
get {
if (fiscal == null) {
fiscal = new List<String>();
Schema.DescribeFieldResult field = Monthly_Activity__c.Fiscal_Quarter__c.getDescribe();
for (Schema.PicklistEntry f : field.getPicklistValues())
fiscal.add(f.getLabel());
}
return fiscal;
}
set;
}
//Approval Code
public void beginApprovalProcess(Monthly_Activity__c monAct){
Approval.ProcessSubmitRequest req = new Approval.ProcessSubmitRequest();
req.setComments('Submitting Records for Approval via VisualForce Page');
req.setObjectId(monAct.id);
}
// Button Code
public void submitForApproval(){
if(fiscalPeriod <> Null && yearValue <> Null) {
if(UserInfo.getProfileId()== submitterProfileId || UserInfo.getProfileId()== adminProfileId){
//beginApprovalProcess();
}
}
}
public PageReference Cancel(){
PageReference page = new PageReference('/FormerAuthorPage');
page.setRedirect(true);
return page;
}
public PageReference Approve(){
if(fiscalPeriod <> Null && yearValue <> Null) {
if(UserInfo.getProfileId()== financeProfileId || UserInfo.getProfileId()== editorialProfileId){
}
}
PageReference page = new PageReference('/FormerAuthorPage');
page.setRedirect(true);
return page;
}
public PageReference Reject(){
if(fiscalPeriod <> Null && yearValue <> Null) {
if(UserInfo.getProfileId()== financeProfileId || UserInfo.getProfileId()== editorialProfileId){
}
}
PageReference page = new PageReference('/FormerAuthorPage');
page.setRedirect(true);
return page;
}
// Pagination Code
public PageReference Beginning() { //user clicked first
counter = 0;
runQuery();
return null;
}
public PageReference Previous() { //user clicked previous
counter -= list_size;
runQuery();
return null;
}
public PageReference Next() { //user clicked next
counter += list_size;
runQuery();
return null;
}
public PageReference End() { //user clicked last
counter = total_size - math.mod(total_size, list_size);
runQuery();
return null;
}
public Boolean getDisablePrevious() {
//this will disable the previous and beginning buttons
if (counter>0) return false;
else return true;
}
public Boolean getDisableNext() { //this will disable the next and end buttons
if (counter + list_size < total_size)
return false; else return true;
}
public Integer getTotal_size() {
return total_size;
}
public Integer getPageNumber() {
return counter/list_size + 1;
}
public Integer getTotalPages() {
if (math.mod(total_size, list_size) > 0) {
return total_size/list_size + 1;
} else {
return (total_size/list_size);
}
}
}
Component :
<apex:component >
<script>
function cvCheckAllOrNone(allOrNoneCheckbox) {
// Find parent table
var container = allOrNoneCheckbox;
while (container.tagName != "TABLE") {
container = container.parentNode;
}
// Switch all checkboxes
var inputs = container.getElementsByTagName("input");
var checked = allOrNoneCheckbox.checked;
for (var i = 0; i < inputs.length; i++) {
var input = inputs.item(i);
if (input.type == "checkbox") {
if (input != allOrNoneCheckbox) {
input.checked = checked;
}
}
}
}
</script>
<apex:inputCheckbox onclick="cvCheckAllOrNone(this)" title="Toggle All Rows"/>
Any help in this matter is greatly appreciated. Thanks!