328 lines
11 KiB
OpenEdge ABL
328 lines
11 KiB
OpenEdge ABL
/**
|
|
* @description Main invocable class for combining multiple Docusign templates into a single envelope
|
|
* @author Paul Huliganga
|
|
* @date 2026-02-23
|
|
*/
|
|
global with sharing class DocusignCompositeEnvelopeBuilder {
|
|
|
|
/**
|
|
* @description Invocable method called from Salesforce Screen Flow
|
|
* @param requests List of request objects containing template IDs and metadata
|
|
* @return List of result objects with envelope ID and status
|
|
*/
|
|
@InvocableMethod(
|
|
label='Send Composite Docusign Envelope'
|
|
description='Combines multiple Docusign templates into a single envelope'
|
|
category='Docusign'
|
|
)
|
|
public static List<Result> sendCompositeEnvelope(List<Request> requests) {
|
|
List<Result> results = new List<Result>();
|
|
|
|
// Process first request (Flow only sends one)
|
|
if (requests == null || requests.isEmpty()) {
|
|
return buildErrorResult('No request provided');
|
|
}
|
|
|
|
Request req = requests[0];
|
|
Result result = new Result();
|
|
|
|
try {
|
|
// Validate inputs
|
|
validateInputs(req);
|
|
|
|
// Remove duplicates and sort alphabetically
|
|
List<String> sortedTemplateIds = sortTemplatesAlphabetically(
|
|
new Set<String>(req.templateIds)
|
|
);
|
|
|
|
// Build composite envelope JSON
|
|
String envelopeJSON = buildCompositeEnvelopeJSON(
|
|
sortedTemplateIds,
|
|
req.recordId,
|
|
req.language,
|
|
req.emailSubject,
|
|
null // customFields not supported in InvocableVariable (Phase 2 enhancement)
|
|
);
|
|
|
|
// Get Docusign credentials
|
|
DocusignCredentials creds = DocusignCredentials.getInstance();
|
|
|
|
// Call Docusign API
|
|
String envelopeId = DocusignAPIService.createCompositeEnvelope(
|
|
envelopeJSON,
|
|
creds
|
|
);
|
|
|
|
// Success
|
|
result.envelopeId = envelopeId;
|
|
result.success = true;
|
|
result.errorMessage = null;
|
|
|
|
// Log success
|
|
logAPICall(req.templateIds.size(), envelopeId, 'Success', null);
|
|
|
|
} catch (Exception e) {
|
|
// Error handling
|
|
result.success = false;
|
|
result.errorMessage = e.getMessage();
|
|
result.envelopeId = null;
|
|
|
|
// Log error
|
|
logAPICall(
|
|
req.templateIds != null ? req.templateIds.size() : 0,
|
|
null,
|
|
'Error',
|
|
e.getMessage() + '\n' + e.getStackTraceString()
|
|
);
|
|
|
|
// Re-throw if critical (governor limits)
|
|
if (e instanceof System.LimitException) {
|
|
throw e;
|
|
}
|
|
}
|
|
|
|
results.add(result);
|
|
return results;
|
|
}
|
|
|
|
/**
|
|
* @description Validates input parameters
|
|
* @param req Request object to validate
|
|
* @throws IllegalArgumentException if validation fails
|
|
*/
|
|
private static void validateInputs(Request req) {
|
|
if (req.templateIds == null || req.templateIds.isEmpty()) {
|
|
throw new IllegalArgumentException('At least one template ID is required');
|
|
}
|
|
|
|
if (req.templateIds.size() > 14) {
|
|
throw new IllegalArgumentException('Maximum 14 templates allowed per envelope');
|
|
}
|
|
|
|
if (String.isBlank(req.recordId)) {
|
|
throw new IllegalArgumentException('Salesforce record ID is required');
|
|
}
|
|
|
|
// Check for null template IDs
|
|
for (String templateId : req.templateIds) {
|
|
if (String.isBlank(templateId)) {
|
|
throw new IllegalArgumentException('Template ID cannot be blank');
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @description Removes duplicates and sorts template IDs alphabetically
|
|
* @param templateIdSet Set of template IDs
|
|
* @return Sorted list of unique template IDs
|
|
*/
|
|
private static List<String> sortTemplatesAlphabetically(Set<String> templateIdSet) {
|
|
List<String> sortedList = new List<String>(templateIdSet);
|
|
sortedList.sort();
|
|
return sortedList;
|
|
}
|
|
|
|
/**
|
|
* @description Builds composite envelope JSON for Docusign API
|
|
* @param templateIds List of template IDs to combine
|
|
* @param recordId Salesforce record ID for custom fields
|
|
* @param language Language code (en/es)
|
|
* @param emailSubject Email subject line
|
|
* @param customFields Map of custom field name/value pairs
|
|
* @return JSON string for Docusign API request
|
|
*/
|
|
@TestVisible
|
|
private static String buildCompositeEnvelopeJSON(
|
|
List<String> templateIds,
|
|
String recordId,
|
|
String language,
|
|
String emailSubject,
|
|
Map<String, String> customFields
|
|
) {
|
|
// Build composite templates array
|
|
List<Object> compositeTemplates = new List<Object>();
|
|
|
|
Integer sequence = 1;
|
|
for (String templateId : templateIds) {
|
|
Map<String, Object> compositeTemplate = new Map<String, Object>{
|
|
'compositeTemplateId' => String.valueOf(sequence),
|
|
'serverTemplates' => new List<Object>{
|
|
new Map<String, Object>{
|
|
'sequence' => String.valueOf(sequence),
|
|
'templateId' => templateId
|
|
}
|
|
}
|
|
};
|
|
|
|
// Add custom fields if this is the first template
|
|
if (sequence == 1 && (String.isNotBlank(recordId) || customFields != null)) {
|
|
compositeTemplate.put('inlineTemplates', buildInlineTemplates(recordId, language, customFields));
|
|
}
|
|
|
|
compositeTemplates.add(compositeTemplate);
|
|
sequence++;
|
|
}
|
|
|
|
// Build envelope object
|
|
Map<String, Object> envelope = new Map<String, Object>{
|
|
'status' => 'sent',
|
|
'emailSubject' => String.isNotBlank(emailSubject)
|
|
? emailSubject
|
|
: 'Please review and sign these forms',
|
|
'compositeTemplates' => compositeTemplates
|
|
};
|
|
|
|
return JSON.serialize(envelope);
|
|
}
|
|
|
|
/**
|
|
* @description Builds inline templates for custom fields
|
|
* @param recordId Salesforce record ID
|
|
* @param language Language code
|
|
* @param customFields Additional custom fields
|
|
* @return List of inline template objects
|
|
*/
|
|
private static List<Object> buildInlineTemplates(
|
|
String recordId,
|
|
String language,
|
|
Map<String, String> customFields
|
|
) {
|
|
List<Object> textCustomFields = new List<Object>();
|
|
|
|
// Add Salesforce record ID
|
|
if (String.isNotBlank(recordId)) {
|
|
textCustomFields.add(new Map<String, Object>{
|
|
'name' => 'SalesforceRecordId',
|
|
'value' => recordId,
|
|
'show' => 'false',
|
|
'required' => 'false'
|
|
});
|
|
}
|
|
|
|
// Add language
|
|
if (String.isNotBlank(language)) {
|
|
textCustomFields.add(new Map<String, Object>{
|
|
'name' => 'Language',
|
|
'value' => language,
|
|
'show' => 'false',
|
|
'required' => 'false'
|
|
});
|
|
}
|
|
|
|
// Add additional custom fields
|
|
if (customFields != null && !customFields.isEmpty()) {
|
|
for (String fieldName : customFields.keySet()) {
|
|
textCustomFields.add(new Map<String, Object>{
|
|
'name' => fieldName,
|
|
'value' => customFields.get(fieldName),
|
|
'show' => 'false',
|
|
'required' => 'false'
|
|
});
|
|
}
|
|
}
|
|
|
|
return new List<Object>{
|
|
new Map<String, Object>{
|
|
'sequence' => '1',
|
|
'customFields' => new Map<String, Object>{
|
|
'textCustomFields' => textCustomFields
|
|
}
|
|
}
|
|
};
|
|
}
|
|
|
|
/**
|
|
* @description Logs API call to debug log (future: custom object)
|
|
* @param templateCount Number of templates in envelope
|
|
* @param envelopeId Docusign envelope ID
|
|
* @param status Success or Error
|
|
* @param errorMessage Error message if applicable
|
|
*/
|
|
private static void logAPICall(
|
|
Integer templateCount,
|
|
String envelopeId,
|
|
String status,
|
|
String errorMessage
|
|
) {
|
|
System.debug(LoggingLevel.INFO, '=== Docusign Composite Envelope API Call ===');
|
|
System.debug(LoggingLevel.INFO, 'Timestamp: ' + System.now());
|
|
System.debug(LoggingLevel.INFO, 'Template Count: ' + templateCount);
|
|
System.debug(LoggingLevel.INFO, 'Envelope ID: ' + envelopeId);
|
|
System.debug(LoggingLevel.INFO, 'Status: ' + status);
|
|
if (String.isNotBlank(errorMessage)) {
|
|
System.debug(LoggingLevel.ERROR, 'Error: ' + errorMessage);
|
|
}
|
|
|
|
// Future enhancement: Insert into Docusign_API_Log__c custom object
|
|
}
|
|
|
|
/**
|
|
* @description Helper to build error result
|
|
* @param errorMessage Error message
|
|
* @return List containing single error result
|
|
*/
|
|
private static List<Result> buildErrorResult(String errorMessage) {
|
|
Result result = new Result();
|
|
result.success = false;
|
|
result.errorMessage = errorMessage;
|
|
result.envelopeId = null;
|
|
return new List<Result>{ result };
|
|
}
|
|
|
|
/**
|
|
* @description Input parameters for invocable method (from Screen Flow)
|
|
*/
|
|
global class Request {
|
|
@InvocableVariable(
|
|
label='Template IDs'
|
|
description='List of Docusign template IDs to combine'
|
|
required=true
|
|
)
|
|
public List<String> templateIds;
|
|
|
|
@InvocableVariable(
|
|
label='Salesforce Record ID'
|
|
description='ID of the Salesforce record to attach documents to'
|
|
required=true
|
|
)
|
|
public String recordId;
|
|
|
|
@InvocableVariable(
|
|
label='Language'
|
|
description='Language code (en or es)'
|
|
required=false
|
|
)
|
|
public String language;
|
|
|
|
@InvocableVariable(
|
|
label='Email Subject'
|
|
description='Subject line for envelope email'
|
|
required=false
|
|
)
|
|
public String emailSubject;
|
|
}
|
|
|
|
/**
|
|
* @description Output parameters for invocable method (to Screen Flow)
|
|
*/
|
|
global class Result {
|
|
@InvocableVariable(
|
|
label='Envelope ID'
|
|
description='Docusign envelope ID'
|
|
)
|
|
public String envelopeId;
|
|
|
|
@InvocableVariable(
|
|
label='Success'
|
|
description='True if envelope was created successfully'
|
|
)
|
|
public Boolean success;
|
|
|
|
@InvocableVariable(
|
|
label='Error Message'
|
|
description='Error message if envelope creation failed'
|
|
)
|
|
public String errorMessage;
|
|
}
|
|
}
|