diff --git a/IMPLEMENTATION_PLAN.md b/IMPLEMENTATION_PLAN.md index 89ab987..9d027bc 100644 --- a/IMPLEMENTATION_PLAN.md +++ b/IMPLEMENTATION_PLAN.md @@ -10,7 +10,7 @@ - [x] **Task 1 — Custom fields:** Add 5 eSignature tracking fields to `Appraiser_Case__c` object metadata and update `package.xml` if needed. Deploy to verify. (FR-001) -- [ ] **Task 2 — EnvelopeCreateResult class + createEnvelope() method:** Add `EnvelopeCreateResult` inner class and `createEnvelope(Id caseId, String accountCode, String templateId)` to `DocusignESignatureService`. (FR-002) +- [x] **Task 2 — EnvelopeCreateResult class + createEnvelope() method:** Add `EnvelopeCreateResult` inner class and `createEnvelope(Id caseId, String accountCode, String templateId)` to `DocusignESignatureService`. (FR-002) - [ ] **Task 3 — Tests for createEnvelope():** Add test coverage in `DocusignESignatureServiceTest` — success path (mock 201), failure path (mock 400), blank email guard. (NFR-001, TC-001, TC-002) diff --git a/force-app/main/default/classes/DocusignESignatureService.cls b/force-app/main/default/classes/DocusignESignatureService.cls index 0d14898..b9928d4 100644 --- a/force-app/main/default/classes/DocusignESignatureService.cls +++ b/force-app/main/default/classes/DocusignESignatureService.cls @@ -43,6 +43,14 @@ public with sharing class DocusignESignatureService { @AuraEnabled public String rawJson; } + public class EnvelopeCreateResult { + @AuraEnabled public Boolean success; + @AuraEnabled public String envelopeId; + @AuraEnabled public String status; + @AuraEnabled public String envelopeUri; + @AuraEnabled public String errorMessage; + } + @AuraEnabled(cacheable=true) public static ESignatureAccountConfig getAccountConfig(String accountCode) { CLM_Account_Setting__mdt row = requireAccountSetting(accountCode); @@ -144,6 +152,78 @@ public with sharing class DocusignESignatureService { return parseEnvelopeList(response.responseBody); } + @AuraEnabled(cacheable=false) + public static EnvelopeCreateResult createEnvelope(Id caseId, String accountCode, String templateId) { + EnvelopeCreateResult result = new EnvelopeCreateResult(); + result.success = false; + try { + if (String.isBlank(templateId)) { + result.errorMessage = 'Template ID is required.'; + return result; + } + + CLM_Account_Setting__mdt row = requireAccountSetting(accountCode); + String targetAccountId = requireESignatureAccountId(row, accountCode, null); + + Appraiser_Case__c caseRecord = [ + SELECT Appraiser_Email__c, + Appraiser_Name__c, + Appraiser_Last_Name__c, + Appraiser_Salutation__c + FROM Appraiser_Case__c + WHERE Id = :caseId + LIMIT 1 + ]; + + if (String.isBlank(caseRecord.Appraiser_Email__c)) { + result.errorMessage = 'Appraiser email is blank on this case. Cannot create envelope.'; + return result; + } + + String appraiserName = String.isNotBlank(caseRecord.Appraiser_Name__c) + ? caseRecord.Appraiser_Name__c + : ((String.isNotBlank(caseRecord.Appraiser_Salutation__c) + ? caseRecord.Appraiser_Salutation__c + ' ' + : '') + String.valueOf(caseRecord.Appraiser_Last_Name__c)).trim(); + + Map roleMap = new Map{ + 'email' => caseRecord.Appraiser_Email__c, + 'name' => appraiserName, + 'roleName' => 'Signer' + }; + Map bodyMap = new Map{ + 'templateId' => templateId, + 'templateRoles' => new List{ roleMap }, + 'status' => 'sent' + }; + + HttpRequest req = new HttpRequest(); + req.setEndpoint(buildEndpoint( + '/v2.1/accounts/' + targetAccountId + '/envelopes', + row.ESignature_Rest_Named_Credential__c + )); + req.setMethod('POST'); + req.setHeader('Content-Type', 'application/json'); + req.setTimeout(30000); + req.setBody(JSON.serialize(bodyMap)); + + HttpResponse res = new Http().send(req); + if (res.getStatusCode() >= 200 && res.getStatusCode() < 300) { + Map responseMap = (Map) JSON.deserializeUntyped(res.getBody()); + result.success = true; + result.envelopeId = (String) responseMap.get('envelopeId'); + result.status = (String) responseMap.get('status'); + result.envelopeUri = (String) responseMap.get('uri'); + } else { + result.errorMessage = 'eSignature API Error (HTTP ' + res.getStatusCode() + '): ' + res.getBody(); + } + } catch (Exception e) { + result.success = false; + result.errorMessage = e.getMessage(); + } + return result; + } + @TestVisible private static List parseAccountList(String body) { Object root = JSON.deserializeUntyped(body);