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 Apttus 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.


APISignature
updateApprovalDatawebService static Boolean updateApprovalData(Id instanceId, String sObjectType, Id contextObjId, List oldContextObjIds, List newContextObjIds)
Request Parameters
NameTypeRequired?Description
instanceIdIDYesThe process instance id that is being currently executed in the system.
sObjectTypeStringYesThesObjecttypethat is used to identify the object type.
contextObjIdIDYesThe ID of the context object.
oldContextObjIdsList<ID>oldContextObjIdsYesThe list of old approval data context object ids that are to be replaced with the new ones.
newContextObjIdsList<ID> newContextObjIdsYesThe list of new approval data context object id store place with.
Response Parameter
NameTypeRequiredDescription
OkBooleanYesDefines 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());
	}
}
CODE