feat(apex): support multiple copies of Authorization to Release Information
DocusignEnvelopeRequest: - Add authReleaseFormCopies InvocableVariable (Integer, optional, 1-3) DocusignCompositeEnvelopeBuilder: - Add MULTI_COPY_TEMPLATE_NAME constant (single place to update if the template is renamed; matched via LIKE '%...%' covering both languages) - Before building the document list, query for template IDs matching the multi-copy template name and append (copies-1) additional IDs so the envelope contains the correct number of distinct documents - Duplicate-safe label builder: appends '(Copy N)' suffix to keep document labels unique within the envelope - Build deduplicated displayNames list for email subject/body: collapses multi-copy entries to a single line with a '(N)' count suffix, e.g. 'Authorization to Release Information (3)' - Email body: each template's message text is included only once even when the same template ID appears multiple times (bodyIdsAdded guard) - Log and Status label use displayNames (deduplicated) not raw docNames
This commit is contained in:
parent
81c08f3fe1
commit
9b355b997e
|
|
@ -20,6 +20,15 @@ global with sharing class DocusignCompositeEnvelopeBuilder {
|
||||||
private static final String ROLE_SERVICE_COORDINATOR = 'Service Coordinator';
|
private static final String ROLE_SERVICE_COORDINATOR = 'Service Coordinator';
|
||||||
private static final String ROLE_DOCUSIGN_RECIPIENT = 'Docusign Recipient #1';
|
private static final String ROLE_DOCUSIGN_RECIPIENT = 'Docusign Recipient #1';
|
||||||
|
|
||||||
|
// ============================================================
|
||||||
|
// MULTI-COPY TEMPLATE: Update this if the template name changes.
|
||||||
|
// This is matched against the dfsle__EnvelopeConfiguration__c Name
|
||||||
|
// field using a case-insensitive contains check.
|
||||||
|
// Both English and Spanish versions share this base name.
|
||||||
|
// ============================================================
|
||||||
|
@TestVisible
|
||||||
|
private static final String MULTI_COPY_TEMPLATE_NAME = 'Authorization to Release Information';
|
||||||
|
|
||||||
@InvocableMethod(
|
@InvocableMethod(
|
||||||
label='Send Composite Docusign Envelope'
|
label='Send Composite Docusign Envelope'
|
||||||
description='Combines multiple Docusign templates into a single envelope using dfsle Apex Toolkit'
|
description='Combines multiple Docusign templates into a single envelope using dfsle Apex Toolkit'
|
||||||
|
|
@ -44,8 +53,38 @@ global with sharing class DocusignCompositeEnvelopeBuilder {
|
||||||
new dfsle.Entity(req.recordId)
|
new dfsle.Entity(req.recordId)
|
||||||
);
|
);
|
||||||
|
|
||||||
// Build document list from templates (deduplicated and sorted)
|
// Expand multi-copy templates before deduplication.
|
||||||
List<String> sortedTemplateIds = new List<String>(new Set<String>(req.templateIds));
|
// If the user selected the Authorization to Release Information template and
|
||||||
|
// requested more than 1 copy, insert additional copies of its template ID into
|
||||||
|
// the list now so the deduplication step handles all IDs uniformly.
|
||||||
|
List<String> expandedTemplateIds = new List<String>(req.templateIds);
|
||||||
|
Integer copies = (req.authReleaseFormCopies != null && req.authReleaseFormCopies > 1)
|
||||||
|
? Math.min(req.authReleaseFormCopies, 3)
|
||||||
|
: 1;
|
||||||
|
if (copies > 1) {
|
||||||
|
// Find which template ID(s) correspond to the multi-copy template
|
||||||
|
List<String> multiCopyIds = new List<String>();
|
||||||
|
for (dfsle__EnvelopeConfiguration__c config : [
|
||||||
|
SELECT dfsle__DocuSignId__c
|
||||||
|
FROM dfsle__EnvelopeConfiguration__c
|
||||||
|
WHERE dfsle__DocuSignId__c IN :req.templateIds
|
||||||
|
AND Name LIKE :('%' + MULTI_COPY_TEMPLATE_NAME + '%')
|
||||||
|
]) {
|
||||||
|
multiCopyIds.add(config.dfsle__DocuSignId__c);
|
||||||
|
}
|
||||||
|
// Add (copies - 1) additional entries for each matched template ID
|
||||||
|
for (String multiId : multiCopyIds) {
|
||||||
|
for (Integer i = 1; i < copies; i++) {
|
||||||
|
expandedTemplateIds.add(multiId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build document list from templates.
|
||||||
|
// NOTE: We intentionally do NOT deduplicate here so that multiple copies of
|
||||||
|
// the same template ID produce distinct documents in the envelope.
|
||||||
|
// We sort by label after resolving names instead.
|
||||||
|
List<String> sortedTemplateIds = new List<String>(expandedTemplateIds);
|
||||||
sortedTemplateIds.sort();
|
sortedTemplateIds.sort();
|
||||||
|
|
||||||
// Query template names for document labels (shows in Docusign Status)
|
// Query template names for document labels (shows in Docusign Status)
|
||||||
|
|
@ -65,7 +104,10 @@ global with sharing class DocusignCompositeEnvelopeBuilder {
|
||||||
|
|
||||||
List<dfsle.Document> documents = new List<dfsle.Document>();
|
List<dfsle.Document> documents = new List<dfsle.Document>();
|
||||||
List<String> docNames = new List<String>();
|
List<String> docNames = new List<String>();
|
||||||
Map<String, String> labelToId = new Map<String, String>();
|
// Use a list of label+id pairs to correctly handle duplicate template IDs
|
||||||
|
// (e.g. multiple copies of Authorization to Release Information)
|
||||||
|
List<String[]> labelIdPairs = new List<String[]>();
|
||||||
|
Map<String, Integer> labelCounters = new Map<String, Integer>();
|
||||||
for (String templateId : sortedTemplateIds) {
|
for (String templateId : sortedTemplateIds) {
|
||||||
String label;
|
String label;
|
||||||
if (templateShortNames.containsKey(templateId)) {
|
if (templateShortNames.containsKey(templateId)) {
|
||||||
|
|
@ -75,10 +117,24 @@ global with sharing class DocusignCompositeEnvelopeBuilder {
|
||||||
} else {
|
} else {
|
||||||
label = templateId;
|
label = templateId;
|
||||||
}
|
}
|
||||||
labelToId.put(label, templateId);
|
// If the same label appears more than once, append " (Copy N)" to distinguish
|
||||||
|
if (labelCounters.containsKey(label)) {
|
||||||
|
Integer count = labelCounters.get(label) + 1;
|
||||||
|
labelCounters.put(label, count);
|
||||||
|
label = label + ' (Copy ' + count + ')';
|
||||||
|
} else {
|
||||||
|
labelCounters.put(label, 1);
|
||||||
|
}
|
||||||
|
labelIdPairs.add(new String[]{ label, templateId });
|
||||||
docNames.add(label);
|
docNames.add(label);
|
||||||
}
|
}
|
||||||
|
// Sort by label for consistent ordering
|
||||||
docNames.sort();
|
docNames.sort();
|
||||||
|
// Re-order labelIdPairs to match sorted docNames
|
||||||
|
Map<String, String> labelToId = new Map<String, String>();
|
||||||
|
for (String[] pair : labelIdPairs) {
|
||||||
|
labelToId.put(pair[0], pair[1]);
|
||||||
|
}
|
||||||
List<String> sortedIds = new List<String>();
|
List<String> sortedIds = new List<String>();
|
||||||
for (String label : docNames) {
|
for (String label : docNames) {
|
||||||
sortedIds.add(labelToId.get(label));
|
sortedIds.add(labelToId.get(label));
|
||||||
|
|
@ -95,9 +151,30 @@ global with sharing class DocusignCompositeEnvelopeBuilder {
|
||||||
}
|
}
|
||||||
myEnvelope = myEnvelope.withDocuments(documents);
|
myEnvelope = myEnvelope.withDocuments(documents);
|
||||||
|
|
||||||
|
// Build a deduplicated display list for the email subject and body.
|
||||||
|
// Where a template appears more than once (multi-copy), show the base label
|
||||||
|
// once with a " [x N]" count suffix, e.g. "Authorization to Release Information [x 3]".
|
||||||
|
// This keeps the subject and body clean while the envelope still contains all copies.
|
||||||
|
Map<String, Integer> baseNameCounts = new Map<String, Integer>();
|
||||||
|
List<String> baseNameOrder = new List<String>();
|
||||||
|
for (String label : docNames) {
|
||||||
|
// Strip the " (Copy N)" suffix to recover the base label
|
||||||
|
String baseName = label.replaceAll(' \\(Copy \\d+\\)$', '');
|
||||||
|
if (!baseNameCounts.containsKey(baseName)) {
|
||||||
|
baseNameCounts.put(baseName, 0);
|
||||||
|
baseNameOrder.add(baseName);
|
||||||
|
}
|
||||||
|
baseNameCounts.put(baseName, baseNameCounts.get(baseName) + 1);
|
||||||
|
}
|
||||||
|
List<String> displayNames = new List<String>();
|
||||||
|
for (String baseName : baseNameOrder) {
|
||||||
|
Integer cnt = baseNameCounts.get(baseName);
|
||||||
|
displayNames.add(cnt > 1 ? baseName + ' (' + cnt + ')' : baseName);
|
||||||
|
}
|
||||||
|
|
||||||
// Set combined template names as the envelope document name
|
// Set combined template names as the envelope document name
|
||||||
// (shows in Docusign Status "Document Name" column)
|
// (shows in Docusign Status "Document Name" column)
|
||||||
String combinedName = String.join(docNames, ', ');
|
String combinedName = String.join(displayNames, ', ');
|
||||||
if (combinedName.length() > 255) {
|
if (combinedName.length() > 255) {
|
||||||
combinedName = combinedName.left(252) + '...';
|
combinedName = combinedName.left(252) + '...';
|
||||||
}
|
}
|
||||||
|
|
@ -114,22 +191,27 @@ global with sharing class DocusignCompositeEnvelopeBuilder {
|
||||||
List<dfsle.Recipient> recipients = resolveRecipients(req.recordId);
|
List<dfsle.Recipient> recipients = resolveRecipients(req.recordId);
|
||||||
myEnvelope = myEnvelope.withRecipients(recipients);
|
myEnvelope = myEnvelope.withRecipients(recipients);
|
||||||
|
|
||||||
// Set envelope subject to combined template names and body to concatenated template email messages
|
// Set envelope subject to combined display names (deduplicated, with copy counts).
|
||||||
// Query for EmailMessage__c
|
// Query for EmailMessage__c — use a deduplicated set of template IDs so each
|
||||||
|
// template's body text is included only once even when multi-copy is in effect.
|
||||||
|
Set<String> uniqueTemplateIds = new Set<String>(sortedIds);
|
||||||
Map<String, String> templateBodies = new Map<String, String>();
|
Map<String, String> templateBodies = new Map<String, String>();
|
||||||
for (dfsle__EnvelopeConfiguration__c config : [
|
for (dfsle__EnvelopeConfiguration__c config : [
|
||||||
SELECT dfsle__DocuSignId__c, dfsle__EmailMessage__c
|
SELECT dfsle__DocuSignId__c, dfsle__EmailMessage__c
|
||||||
FROM dfsle__EnvelopeConfiguration__c
|
FROM dfsle__EnvelopeConfiguration__c
|
||||||
WHERE dfsle__DocuSignId__c IN :sortedTemplateIds
|
WHERE dfsle__DocuSignId__c IN :uniqueTemplateIds
|
||||||
]) {
|
]) {
|
||||||
if (String.isNotBlank(config.dfsle__EmailMessage__c)) {
|
if (String.isNotBlank(config.dfsle__EmailMessage__c)) {
|
||||||
templateBodies.put(config.dfsle__DocuSignId__c, config.dfsle__EmailMessage__c);
|
templateBodies.put(config.dfsle__DocuSignId__c, config.dfsle__EmailMessage__c);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// Build body using one entry per unique template ID (preserving sorted order)
|
||||||
|
Set<String> bodyIdsAdded = new Set<String>();
|
||||||
List<String> bodyParts = new List<String>();
|
List<String> bodyParts = new List<String>();
|
||||||
for (String templateId : sortedIds) {
|
for (String templateId : sortedIds) {
|
||||||
if (templateBodies.containsKey(templateId)) {
|
if (templateBodies.containsKey(templateId) && !bodyIdsAdded.contains(templateId)) {
|
||||||
bodyParts.add(templateBodies.get(templateId));
|
bodyParts.add(templateBodies.get(templateId));
|
||||||
|
bodyIdsAdded.add(templateId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
String envelopeSubject = combinedName;
|
String envelopeSubject = combinedName;
|
||||||
|
|
@ -148,7 +230,7 @@ global with sharing class DocusignCompositeEnvelopeBuilder {
|
||||||
result.success = true;
|
result.success = true;
|
||||||
result.errorMessage = null;
|
result.errorMessage = null;
|
||||||
|
|
||||||
logResult(sortedTemplateIds.size(), result.envelopeId, 'Success (' + String.join(docNames, ', ') + ')', null);
|
logResult(sortedTemplateIds.size(), result.envelopeId, 'Success (' + String.join(displayNames, ', ') + ')', null);
|
||||||
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
result.success = false;
|
result.success = false;
|
||||||
|
|
|
||||||
|
|
@ -32,4 +32,11 @@ global class DocusignEnvelopeRequest {
|
||||||
required=false
|
required=false
|
||||||
)
|
)
|
||||||
global String emailSubject;
|
global String emailSubject;
|
||||||
|
|
||||||
|
@InvocableVariable(
|
||||||
|
label='Authorization to Release Form Copies'
|
||||||
|
description='Number of times to include the Authorization to Release Information template (1-3). Only used when that template is selected.'
|
||||||
|
required=false
|
||||||
|
)
|
||||||
|
global Integer authReleaseFormCopies;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue