diff --git a/composite-envelope-builder/force-app/main/default/classes/DocusignCompositeEnvelopeBuilder.cls b/composite-envelope-builder/force-app/main/default/classes/DocusignCompositeEnvelopeBuilder.cls index 74852a4..a80b693 100644 --- a/composite-envelope-builder/force-app/main/default/classes/DocusignCompositeEnvelopeBuilder.cls +++ b/composite-envelope-builder/force-app/main/default/classes/DocusignCompositeEnvelopeBuilder.cls @@ -1,11 +1,25 @@ /** * @description Combines multiple Docusign templates into a single composite envelope - * using the dfsle Apex Toolkit (Docusign for Salesforce managed package) + * using the dfsle Apex Toolkit (Docusign for Salesforce managed package). + * Recipients are resolved from Client_Case__c lookup fields. * @author Paul Huliganga * @date 2026-02-25 */ global with sharing class DocusignCompositeEnvelopeBuilder { + // ============================================================ + // CONFIGURATION: Update these constants if field/role names change + // ============================================================ + + // API names of the lookup fields on Client_Case__c that point to recipient records + // These are the "Select Lookup Field" values from the Docusign template recipient config + private static final String FIELD_SERVICE_COORDINATOR = 'Service_Coordinator__c'; + private static final String FIELD_DOCUSIGN_RECIPIENT = 'Docusign_Recipient_1__c'; + + // Role names must match EXACTLY what's configured in the Docusign templates + private static final String ROLE_SERVICE_COORDINATOR = 'Service Coordinator'; + private static final String ROLE_DOCUSIGN_RECIPIENT = 'Docusign Recipient #1'; + @InvocableMethod( label='Send Composite Docusign Envelope' description='Combines multiple Docusign templates into a single envelope using dfsle Apex Toolkit' @@ -25,7 +39,7 @@ global with sharing class DocusignCompositeEnvelopeBuilder { // Validate request DocusignEnvelopeRequestHandler.validateRequest(req); - // Create empty envelope linked to the source Salesforce record + // Create empty envelope linked to the source record dfsle.Envelope myEnvelope = dfsle.EnvelopeService.getEmptyEnvelope( new dfsle.Entity(req.recordId) ); @@ -43,16 +57,18 @@ global with sharing class DocusignCompositeEnvelopeBuilder { ) ); } - - // Add all templates as documents to the envelope myEnvelope = myEnvelope.withDocuments(documents); + // Resolve recipients from Client_Case__c lookup fields + List recipients = resolveRecipients(req.recordId); + myEnvelope = myEnvelope.withRecipients(recipients); + // Set email subject if provided if (String.isNotBlank(req.emailSubject)) { myEnvelope = myEnvelope.withEmail(req.emailSubject, ''); } - // Send the envelope (true = send immediately) + // Send the envelope myEnvelope = dfsle.EnvelopeService.sendEnvelope(myEnvelope, true); // Success @@ -82,6 +98,87 @@ global with sharing class DocusignCompositeEnvelopeBuilder { return results; } + /** + * @description Resolves recipients from Client_Case__c lookup fields. + * Queries the case record and related contacts to get name/email. + * @param recordId The Client_Case__c record ID + * @return List of dfsle.Recipient objects with role mappings + */ + private static List resolveRecipients(String recordId) { + // Query the Client_Case__c record with recipient lookup fields + // NOTE: Adjust field API names if they differ in your org + String query = 'SELECT Id, ' + + FIELD_SERVICE_COORDINATOR + ', ' + + FIELD_DOCUSIGN_RECIPIENT + + ' FROM Client_Case__c WHERE Id = :recordId LIMIT 1'; + + Client_Case__c caseRecord = Database.query(query); + + List recipients = new List(); + Integer routingOrder = 1; + + // Recipient 1: Service Coordinator + Id serviceCoordinatorId = (Id) caseRecord.get(FIELD_SERVICE_COORDINATOR); + if (serviceCoordinatorId != null) { + recipients.add(buildRecipient(serviceCoordinatorId, ROLE_SERVICE_COORDINATOR, routingOrder++, recordId)); + } + + // Recipient 2: Docusign Recipient #1 + Id docusignRecipientId = (Id) caseRecord.get(FIELD_DOCUSIGN_RECIPIENT); + if (docusignRecipientId != null) { + recipients.add(buildRecipient(docusignRecipientId, ROLE_DOCUSIGN_RECIPIENT, routingOrder++, recordId)); + } + + if (recipients.isEmpty()) { + throw new IllegalArgumentException('No recipients found on the Client Case record. ' + + 'Please ensure Service Coordinator and Docusign Recipient #1 are populated.'); + } + + return recipients; + } + + /** + * @description Builds a dfsle.Recipient from a Contact/User lookup ID + * @param recipientId The Contact or User record ID + * @param roleName The Docusign template role name + * @param routingOrder Signing order + * @param sourceRecordId The source Client_Case__c record ID + * @return dfsle.Recipient configured for the role + */ + private static dfsle.Recipient buildRecipient(Id recipientId, String roleName, Integer routingOrder, String sourceRecordId) { + // Determine if this is a Contact or User + String objectType = recipientId.getSObjectType().getDescribe().getName(); + + String recipientName; + String recipientEmail; + + if (objectType == 'Contact') { + Contact c = [SELECT Id, Name, Email FROM Contact WHERE Id = :recipientId LIMIT 1]; + recipientName = c.Name; + recipientEmail = c.Email; + } else if (objectType == 'User') { + User u = [SELECT Id, Name, Email FROM User WHERE Id = :recipientId LIMIT 1]; + recipientName = u.Name; + recipientEmail = u.Email; + } else { + throw new IllegalArgumentException('Unsupported recipient type: ' + objectType + + '. Expected Contact or User.'); + } + + if (String.isBlank(recipientEmail)) { + throw new IllegalArgumentException('No email found for ' + roleName + ' (' + recipientName + '). ' + + 'Please ensure the recipient has a valid email address.'); + } + + return dfsle.Recipient.fromSource( + recipientName, + recipientEmail, + null, // phone (optional) + roleName, // must match template role exactly + new dfsle.Entity(sourceRecordId) // source record for merge fields + ); + } + private static void logResult(Integer templateCount, String envelopeId, String status, String errorMessage) { System.debug(LoggingLevel.INFO, '=== Docusign Composite Envelope ==='); System.debug(LoggingLevel.INFO, 'Templates: ' + templateCount);