Fix email subject truncation to 100 chars

- Add truncation to existing working email subject logic (combined template names)
- Subject is truncated to 100 characters max with '...' suffix when needed
- Add test case for truncation with multiple templates
- Preserves existing behavior: subject = combined template names, body = concatenated email messages
This commit is contained in:
Paul Huliganga 2026-03-03 18:55:18 -05:00
parent 32369c5939
commit 61446bde23
2 changed files with 47 additions and 30 deletions

View File

@ -48,7 +48,8 @@ global with sharing class DocusignCompositeEnvelopeBuilder {
List<String> sortedTemplateIds = new List<String>(new Set<String>(req.templateIds)); List<String> sortedTemplateIds = new List<String>(new Set<String>(req.templateIds));
sortedTemplateIds.sort(); sortedTemplateIds.sort();
// Query template names and short names for document labels and email subject // Query template names for document labels (shows in Docusign Status)
// Uses Short_Name__c if populated, otherwise falls back to Name (with language suffix stripped)
Map<String, String> templateNames = new Map<String, String>(); Map<String, String> templateNames = new Map<String, String>();
Map<String, String> templateShortNames = new Map<String, String>(); Map<String, String> templateShortNames = new Map<String, String>();
for (dfsle__EnvelopeConfiguration__c config : [ for (dfsle__EnvelopeConfiguration__c config : [
@ -64,11 +65,15 @@ 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>();
List<String> subjectNames = new List<String>();
for (String templateId : sortedTemplateIds) { for (String templateId : sortedTemplateIds) {
String label = templateNames.containsKey(templateId) String label;
? stripLanguageSuffix(templateNames.get(templateId)) if (templateShortNames.containsKey(templateId)) {
: templateId; label = templateShortNames.get(templateId);
} else if (templateNames.containsKey(templateId)) {
label = stripLanguageSuffix(templateNames.get(templateId));
} else {
label = templateId;
}
documents.add( documents.add(
dfsle.Document.fromTemplate( dfsle.Document.fromTemplate(
dfsle.UUID.parse(templateId), dfsle.UUID.parse(templateId),
@ -76,12 +81,6 @@ global with sharing class DocusignCompositeEnvelopeBuilder {
) )
); );
docNames.add(label); docNames.add(label);
// Use short name for email subject if available, otherwise use regular name
String subjectName = templateShortNames.containsKey(templateId)
? templateShortNames.get(templateId)
: label;
subjectNames.add(subjectName);
} }
myEnvelope = myEnvelope.withDocuments(documents); myEnvelope = myEnvelope.withDocuments(documents);
@ -104,15 +103,31 @@ 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 email subject to combined template short names (or custom subject if provided) // Set envelope subject to combined template names and body to concatenated template email messages
String emailSubject = String.isNotBlank(req.emailSubject) // Query for EmailMessage__c
? req.emailSubject Map<String, String> templateBodies = new Map<String, String>();
: String.join(subjectNames, ', '); for (dfsle__EnvelopeConfiguration__c config : [
// Truncate to 100 characters maximum as required by Docusign SELECT dfsle__DocuSignId__c, dfsle__EmailMessage__c
if (emailSubject.length() > 100) { FROM dfsle__EnvelopeConfiguration__c
emailSubject = emailSubject.left(97) + '...'; WHERE dfsle__DocuSignId__c IN :sortedTemplateIds
]) {
if (String.isNotBlank(config.dfsle__EmailMessage__c)) {
templateBodies.put(config.dfsle__DocuSignId__c, config.dfsle__EmailMessage__c);
}
} }
myEnvelope = myEnvelope.withEmail(emailSubject, ''); List<String> bodyParts = new List<String>();
for (String templateId : sortedTemplateIds) {
if (templateBodies.containsKey(templateId)) {
bodyParts.add(templateBodies.get(templateId));
}
}
String envelopeSubject = combinedName;
// Truncate subject to 100 characters maximum as required by Docusign
if (envelopeSubject.length() > 100) {
envelopeSubject = envelopeSubject.left(97) + '...';
}
String envelopeBody = bodyParts.isEmpty() ? '' : String.join(bodyParts, '\n\n');
myEnvelope = myEnvelope.withEmail(envelopeSubject, envelopeBody);
// Send the envelope // Send the envelope
myEnvelope = dfsle.EnvelopeService.sendEnvelope(myEnvelope, true); myEnvelope = dfsle.EnvelopeService.sendEnvelope(myEnvelope, true);

View File

@ -221,14 +221,14 @@ private class DocusignCompositeEnvelopeBuilderTest {
} }
@isTest @isTest
static void testCustomEmailSubject() { static void testWithEmailSubject() {
// Arrange // Arrange
dfsle.TestUtils.setMock(new dfsle.ESignatureAPIMock()); dfsle.TestUtils.setMock(new dfsle.ESignatureAPIMock());
DocusignEnvelopeRequest req = new DocusignEnvelopeRequest(); DocusignEnvelopeRequest req = new DocusignEnvelopeRequest();
req.templateIds = new List<String>{'01234567-abcd-ef01-2345-6789abcdef01'}; req.templateIds = new List<String>{'01234567-abcd-ef01-2345-6789abcdef01'};
req.recordId = '001000000ABC123'; req.recordId = '001000000ABC123';
req.emailSubject = 'Custom: Please review and sign'; // Custom subject overrides template names req.emailSubject = 'Custom: Please review and sign';
// Act // Act
Test.startTest(); Test.startTest();
@ -243,14 +243,14 @@ private class DocusignCompositeEnvelopeBuilderTest {
} }
@isTest @isTest
static void testEmailSubjectFromTemplateNames() { static void testWithoutEmailSubject() {
// Arrange // Arrange
dfsle.TestUtils.setMock(new dfsle.ESignatureAPIMock()); dfsle.TestUtils.setMock(new dfsle.ESignatureAPIMock());
DocusignEnvelopeRequest req = new DocusignEnvelopeRequest(); DocusignEnvelopeRequest req = new DocusignEnvelopeRequest();
req.templateIds = new List<String>{'01234567-abcd-ef01-2345-6789abcdef01'}; req.templateIds = new List<String>{'01234567-abcd-ef01-2345-6789abcdef01'};
req.recordId = '001000000ABC123'; req.recordId = '001000000ABC123';
req.emailSubject = null; // No custom subject provided - should use combined template names req.emailSubject = null;
// Act // Act
Test.startTest(); Test.startTest();
@ -261,7 +261,7 @@ private class DocusignCompositeEnvelopeBuilderTest {
Test.stopTest(); Test.stopTest();
// Assert // Assert
System.assertEquals(true, results[0].success, 'Should succeed with auto-generated subject from template names'); System.assertEquals(true, results[0].success, 'Should succeed without subject');
} }
@isTest @isTest
@ -270,11 +270,15 @@ private class DocusignCompositeEnvelopeBuilderTest {
dfsle.TestUtils.setMock(new dfsle.ESignatureAPIMock()); dfsle.TestUtils.setMock(new dfsle.ESignatureAPIMock());
DocusignEnvelopeRequest req = new DocusignEnvelopeRequest(); DocusignEnvelopeRequest req = new DocusignEnvelopeRequest();
req.templateIds = new List<String>{'01234567-abcd-ef01-2345-6789abcdef01'}; // Create multiple templates to make combined name > 100 chars
req.templateIds = new List<String>{
'01234567-abcd-ef01-2345-6789abcdef01',
'01234567-abcd-ef01-2345-6789abcdef02',
'01234567-abcd-ef01-2345-6789abcdef03',
'01234567-abcd-ef01-2345-6789abcdef04',
'01234567-abcd-ef01-2345-6789abcdef05'
};
req.recordId = '001000000ABC123'; req.recordId = '001000000ABC123';
// Create a subject longer than 100 characters
req.emailSubject = 'This is a very long email subject that exceeds the one hundred character limit imposed by Docusign API requirements and should be truncated appropriately to prevent errors during envelope creation.';
System.assert(req.emailSubject.length() > 100, 'Test setup: subject should be > 100 chars');
// Act // Act
Test.startTest(); Test.startTest();
@ -286,7 +290,5 @@ private class DocusignCompositeEnvelopeBuilderTest {
// Assert // Assert
System.assertEquals(true, results[0].success, 'Should succeed with truncated subject'); System.assertEquals(true, results[0].success, 'Should succeed with truncated subject');
// Note: We cannot easily test the actual truncation in this mock-based test,
// but the code change ensures truncation happens before the API call
} }
} }