Download PDF
Download page Enabling Auto Re-Approvals on Proposal and Proposal Line Items.
Enabling Auto Re-Approvals on Proposal and Proposal Line Items
This API takes several parameters and updates the reapproval data in the process instance so that proposal line items can be automatically reapproved when resubmitted after finalizing new items in the cart. Note that this API is available in the base Approvals package and can be used with the optional unmanaged custom package "Apttus Proposal Auto-Reapprovals" available on request with Conga Release Management. This package contains the complete sample code that can used along with the API in the base package to make auto-reapprovals work with Proposals and Proposal Line Items.
API | Signature |
---|---|
updateApprovalData | webService static Boolean updateApprovalData(Id instanceId, String sObjectType, Id contextObjId, List oldContextObjIds, List newContextObjIds) |
Request Parameters | |||
---|---|---|---|
Name | Type | Required? | Description |
instanceId | ID | Yes | The process instance id that is being currently executed in the system. |
sObjectType | String | Yes | The sObject type that is used to identify the object type. |
contextObjId | ID | Yes | The ID of the context object. |
oldContextObjIds | List<ID>oldContextObjIds | Yes | The list of old approval data context object ids that are to be replaced with the new ones. |
newContextObjIds | List<ID> newContextObjIds | Yes | The list of new approval data context object id store place with. |
Response Parameter | |||
---|---|---|---|
Name | Type | Required | Description |
Ok | Boolean | Yes | Defines if the operation was successful. Returns true if yes, otherwise false. |
Code Sample
The sample below enables you to execute auto re-approvals over Proposal Line Items by considering the input parameters, such as process instance, sObject type, context object, and lists of old and new context object ids. These IDs represent the Proposal Line Item IDs before and after the cart is finalized. You can use this API in a scenario where a Quote/Proposal is approved but the Approvals Manager decides to change the discount from 10 to 15% on a Proposal Line Item, you can resubmit the approval request for this for re-approval. The system honors the auto-reapproval criteria, using which a request once approved by certain assignees are auto-approved.
See the complete code included in the package to understand how this works, especially CustomProposalLineItemSupport.cls. Note that the code in this package is based on a trigger on the Proposal Line Item object which saves the old ids of the Proposal Line Items before they are deleted as well as the new ids of the Proposal Line Items that get inserted after the cart is finalized. You can extend this concept for other custom child objects to the proposal object by creating a trigger on them and using the same pattern in the sample code to save ids before and after new items are created.
/**
*Apttus Approvals Management
*CustomProposalLineItemTrigger
*@2017 Apttus Inc. All rights reserved.
*/
trigger CustomProposalLineItemTrigger on Apttus_Proposal__Proposal_Line_Item__c (before delete, after insert)
{
if (Trigger.isBefore && Trigger.isDelete)
{
// save map of old line items to attributes before they are deleted
CustomProposalLineItemSupport.doBeforeDelete(Trigger.old, Trigger.oldMap);
}
if (Trigger.isAfter && Trigger.isInsert)
{
// update reapproval data with new line items after they are inserted
CustomProposalLineItemSupport.doAfterInsert(Trigger.new, Trigger.newMap);
}
}
/**
*Apttus Approvals Management
*CustomProposalLineItemSupport
* @2017 Apttus Inc. All rights reserved.
*/
public with sharing class CustomProposalLineItemSupport extends CustomApprovalsConstants
{
// line item types
private static final String LINETYPE_PRODUCT = 'Product/Service';
private static final String LINETYPE_OPTION = 'Option';
// proposal
private static final String PROPOSAL_SOBJECT_TYPE = 'Apttus_Proposal__Proposal__c';
private static ID quoteId = null;
// associated process instance
private static Apttus_Approval__ApprovalProcessInstance__c quoteProcessInstance = null;
// map of old line item ids to attribute key
private static Map<ID,String> oldLineId2KeyMap = new Map<ID,String>();
// map of new line item attribute key to id
private static Map<String,ID> newLineKey2IdMap = new Map<String,ID>();
// map of old line item ids to new line item ids
private static Map<ID,ID> lineOldId2NewIdMap = new Map<ID,ID>();
/**
* Process old ProposalLineItems before they are deleted when a cart is finalized
* @param oldLineItems - a list of the old versions of the sObject records
* @param oldLineItemsMap - a map of IDs to the old versions of the sObject records
*/
public static void doBeforeDelete( List<Apttus_Proposal__Proposal_Line_Item__c> oldLineItems, Map<ID, Apttus_Proposal__Proposal_Line_Item__c> oldLineItemsMap)
{
// iterate over line items about to deleted
for (Integer i=0; i<oldLineItems.size(); i++)
{
Apttus_Proposal__Proposal_Line_Item__c oldLineItem = oldLineItems[i];
// get quote from line item
quoteId = oldLineItem.Apttus_Proposal__Proposal__c;
// get line item attributes
String lineNumber = String.valueOf(oldLineItem.Apttus_QPConfig__PrimaryLineNumber__c);
String lineType = oldLineItem.Apttus_QPConfig__LineType__c;
String productId = null;
if (lineType == LINETYPE_PRODUCT)
{
productId = oldLineItem.Apttus_Proposal__Product__c;
}
else if (lineType == LINETYPE_OPTION)
{
productId = oldLineItem.Apttus_QPConfig__OptionId__c;
//productId = oldLineItem.Apttus_QPConfig__ProductOptionId__c;
}
String chargeType = oldLineItem.Apttus_QPConfig__ChargeType__c;
// create attribute key
String oldLineKey = lineNumber + ':' + productId + ':' + chargeType;
// save in map
oldLineId2KeyMap.put(oldLineItem.Id, oldLineKey);
}
}
/**
* Process new ProposalLineItems after they are inserted when a cart is finalized
* @param newLineItems - a list of the new versions of the sObject records
* @param newLineItemsMap - a map of IDs to the new versions of the sObject records
*/
public static void doAfterInsert(List<Apttus_Proposal__Proposal_Line_Item__c> newLineItems, Map<ID, Apttus_Proposal__Proposal_Line_Item__c> newLineItemsMap)
{
// iterate over line items about to deleted
for (Integer i=0; i<newLineItems.size(); i++)
{
Apttus_Proposal__Proposal_Line_Item__c newLineItem = newLineItems[i];
// get quote from line item
quoteId = newLineItem.Apttus_Proposal__Proposal__c;
// get line item attributes
String lineNumber = String.valueOf(newLineItem.Apttus_QPConfig__PrimaryLineNumber__c);
String lineType = newLineItem.Apttus_QPConfig__LineType__c;
String productId = null;
if (lineType == LINETYPE_PRODUCT)
{
productId = newLineItem.Apttus_Proposal__Product__c;
}
else if(lineType == LINETYPE_OPTION)
{
productId = newLineItem.Apttus_QPConfig__OptionId__c;
//productId = newLineItem.Apttus_QPConfig__ProductOptionId__c;
}
String chargeType = newLineItem.Apttus_QPConfig__ChargeType__c;
// create attribute key
String newLineKey = lineNumber + ':' + productId + ':' + chargeType;
// save in map
newLineKey2IdMap.put(newLineKey, newLineItem.Id);
}
// create map of old line item ids to new line item ids
Set<ID> oldLineItemIds = oldLineId2KeyMap.keySet();
List<ID> newLineItemIds = new List<ID>();
for (String oldLineItemId : oldLineItemIds)
{
// get attribute key
String attrKey = oldLineId2KeyMap.get(oldLineItemId);
// lookup key in new line items map
String newLineItemId = null;
if(newLineKey2IdMap.containsKey(attrKey))
{
newLineItemId = newLineKey2IdMap.get(attrKey);
newLineItemIds.add(newLineItemId);
// associate old key with new one
lineOldId2NewIdMap.put(oldLineItemId, newLineItemId);
}
}
// update reapprovals data by calling API in approvals package
List<ID> oldContextObjIds = new List<ID>(oldLineItemIds);
List<ID> newContextObjIds = newLineItemIds;
system.debug(LoggingLevel.INFO,'sObjectType='+PROPOSAL_SOBJECT_TYPE);
system.debug(LoggingLevel.INFO,'contextObjId='+quoteId);
system.debug(LoggingLevel.INFO,'oldContextObjIds='+oldContextObjIds);
system.debug(LoggingLevel.INFO,'newContextObjIds='+newContextObjIds);
if (!oldContextObjIds.isEmpty() && !newContextObjIds.isEmpty() && oldContextObjIds.size() == newContextObjIds.size())
{
// get process instance associated with the old quote
Apttus_Approval__ApprovalProcessInstance__c instanceSO = getProcessInstance(quoteId);
system.debug(LoggingLevel.INFO,'instanceSO='+instanceSO);
// call API to update reapproval data
Boolean ok = Apttus_Approval.ApprovalsWebService.updateApprovalData(instanceSO.Id, PROPOSAL_SOBJECT_TYPE, quoteId, oldContextObjIds, newContextObjIds);
system.debug(LoggingLevel.INFO,'Apttus_Approval.ApprovalsWebService.updateApprovalData='+ok);
}
}
/**
* Get process instance for the given proposal id
* @param proposalId
* @return process instance object
*/
private static Apttus_Approval__ApprovalProcessInstance__c getProcessInstance(ID proposalId)
{
List<Apttus_Approval__ApprovalProcessInstance__c> instanceList = [select Id, Name, LastModifiedDate, LastModifiedById, LastActivityDate, CreatedDate, CreatedById, Apttus_Approval__Status__c, Apttus_Approval__StartTime__c, Apttus_Approval__ReassignmentEmailTemplate__c, Apttus_Approval__PrevProcessInstanceId__c, Apttus_Approval__NotifyOnlyEmailTemplate__c, Apttus_Approval__InstanceNumber__c, Apttus_Approval__EscalationEmailTemplate__c, Apttus_Approval__EndTime__c, Apttus_Approval__Data__c, Apttus_Approval__ConsolidationVersionNumber__c, Apttus_Approval__CancellationEmailTemplate__c, Apttus_Approval__BusinessObjectType__c, Apttus_Approval__BusinessObjectLink__c, Apttus_Approval__BusinessObjectId__c, Apttus_Approval__AssignmentEmailTemplate__c, Apttus_Approval__ApprovalProcessId__c From Apttus_Approval__ApprovalProcessInstance__c where Apttus_Approval__BusinessObjectId__c = :proposalId order by CreatedDate DESC limit 1];
if ( ! nullOrEmpty(instanceList))
{
return instanceList[0];
}
return null;
}
/**
* Checks if the given string value is null or empty.
* @param strValue the string to check
* @return <code>true</code> if the string value is null or empty, <code>false</code> otherwise
*/
public static Boolean nullOrEmpty(String strValue)
{
// check if null or zero length string
return (strValue == null || strValue.trim().length() == 0);
}
/**
* Checks if the given list of objects is null or empty.
* @param objList the list of objects to check
* @return <code>true</code> if the list is null or empty, <code>false</code> otherwise
*/
public static Boolean nullOrEmpty(List<Object> objList)
{
// check if null or empty
return (objList == null || objList.isEmpty());
}
}