Compare commits
No commits in common. "8510eacecb043b5b448de2c4e8ad34d1fba95f16" and "66be5d83ff51a5e5bfb66b55210e7779384a5b67" have entirely different histories.
8510eacecb
...
66be5d83ff
|
|
@ -8,13 +8,13 @@
|
||||||
|
|
||||||
## Tasks
|
## Tasks
|
||||||
|
|
||||||
- [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 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)
|
||||||
|
|
||||||
- [x] **Task 2 — EnvelopeCreateResult class + createEnvelope() method:** Add `EnvelopeCreateResult` inner class and `createEnvelope(Id caseId, String accountCode, String templateId)` to `DocusignESignatureService`. (FR-002)
|
- [ ] **Task 2 — EnvelopeCreateResult class + createEnvelope() method:** Add `EnvelopeCreateResult` inner class and `createEnvelope(Id caseId, String accountCode, String templateId)` to `DocusignESignatureService`. (FR-002)
|
||||||
|
|
||||||
- [x] **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)
|
- [ ] **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)
|
||||||
|
|
||||||
- [x] **Task 4 — persistEnvelopeResult() + CaseContext extension:** Add `persistEnvelopeResult()` to `CLMAdminService`. Extend `CaseContext` inner class and `getCaseContext()` SOQL to include the 5 eSignature fields. (FR-003, FR-004)
|
- [ ] **Task 4 — persistEnvelopeResult() + CaseContext extension:** Add `persistEnvelopeResult()` to `CLMAdminService`. Extend `CaseContext` inner class and `getCaseContext()` SOQL to include the 5 eSignature fields. (FR-003, FR-004)
|
||||||
|
|
||||||
- [ ] **Task 5 — Tests for persistEnvelopeResult():** Add test coverage in `CLMAdminServiceTest` — verify fields written correctly, `ESignature_Sent_At__c` is set, `ESignature_Completed_At__c` is NOT written on a "sent" result. (NFR-001, TC-003)
|
- [ ] **Task 5 — Tests for persistEnvelopeResult():** Add test coverage in `CLMAdminServiceTest` — verify fields written correctly, `ESignature_Sent_At__c` is set, `ESignature_Completed_At__c` is NOT written on a "sent" result. (NFR-001, TC-003)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -22,11 +22,6 @@ public with sharing class CLMAdminService {
|
||||||
@AuraEnabled public String attachedFileUrl;
|
@AuraEnabled public String attachedFileUrl;
|
||||||
@AuraEnabled public Datetime lastDocGenRequestedAt;
|
@AuraEnabled public Datetime lastDocGenRequestedAt;
|
||||||
@AuraEnabled public Datetime lastDocGenCompletedAt;
|
@AuraEnabled public Datetime lastDocGenCompletedAt;
|
||||||
@AuraEnabled public String eSignatureEnvelopeId;
|
|
||||||
@AuraEnabled public String eSignatureEnvelopeStatus;
|
|
||||||
@AuraEnabled public String eSignatureEnvelopeUrl;
|
|
||||||
@AuraEnabled public Datetime eSignatureSentAt;
|
|
||||||
@AuraEnabled public Datetime eSignatureCompletedAt;
|
|
||||||
@AuraEnabled public List<CaseDeficiencyItem> deficiencies;
|
@AuraEnabled public List<CaseDeficiencyItem> deficiencies;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -318,11 +313,6 @@ public with sharing class CLMAdminService {
|
||||||
Attached_File_Url__c,
|
Attached_File_Url__c,
|
||||||
Last_DocGen_Requested_At__c,
|
Last_DocGen_Requested_At__c,
|
||||||
Last_DocGen_Completed_At__c,
|
Last_DocGen_Completed_At__c,
|
||||||
ESignature_Envelope_Id__c,
|
|
||||||
ESignature_Envelope_Status__c,
|
|
||||||
ESignature_Sent_At__c,
|
|
||||||
ESignature_Completed_At__c,
|
|
||||||
ESignature_Envelope_Url__c,
|
|
||||||
(SELECT Id,
|
(SELECT Id,
|
||||||
Deficiency_Number__c,
|
Deficiency_Number__c,
|
||||||
Description__c,
|
Description__c,
|
||||||
|
|
@ -354,11 +344,6 @@ public with sharing class CLMAdminService {
|
||||||
context.attachedFileUrl = appraiserCase.Attached_File_Url__c;
|
context.attachedFileUrl = appraiserCase.Attached_File_Url__c;
|
||||||
context.lastDocGenRequestedAt = appraiserCase.Last_DocGen_Requested_At__c;
|
context.lastDocGenRequestedAt = appraiserCase.Last_DocGen_Requested_At__c;
|
||||||
context.lastDocGenCompletedAt = appraiserCase.Last_DocGen_Completed_At__c;
|
context.lastDocGenCompletedAt = appraiserCase.Last_DocGen_Completed_At__c;
|
||||||
context.eSignatureEnvelopeId = appraiserCase.ESignature_Envelope_Id__c;
|
|
||||||
context.eSignatureEnvelopeStatus = appraiserCase.ESignature_Envelope_Status__c;
|
|
||||||
context.eSignatureEnvelopeUrl = appraiserCase.ESignature_Envelope_Url__c;
|
|
||||||
context.eSignatureSentAt = appraiserCase.ESignature_Sent_At__c;
|
|
||||||
context.eSignatureCompletedAt = appraiserCase.ESignature_Completed_At__c;
|
|
||||||
context.deficiencies = new List<CaseDeficiencyItem>();
|
context.deficiencies = new List<CaseDeficiencyItem>();
|
||||||
|
|
||||||
if (appraiserCase.Deficiencies__r != null) {
|
if (appraiserCase.Deficiencies__r != null) {
|
||||||
|
|
@ -448,29 +433,6 @@ public with sharing class CLMAdminService {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@AuraEnabled(cacheable=false)
|
|
||||||
public static void persistEnvelopeResult(
|
|
||||||
Id caseId,
|
|
||||||
String envelopeId,
|
|
||||||
String envelopeStatus,
|
|
||||||
String envelopeUri
|
|
||||||
) {
|
|
||||||
if (caseId == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Appraiser_Case__c updateCase = new Appraiser_Case__c(Id = caseId);
|
|
||||||
updateCase.ESignature_Envelope_Id__c = envelopeId;
|
|
||||||
updateCase.ESignature_Envelope_Status__c = envelopeStatus;
|
|
||||||
updateCase.ESignature_Sent_At__c = System.now();
|
|
||||||
|
|
||||||
if (String.isNotBlank(envelopeUri)) {
|
|
||||||
updateCase.ESignature_Envelope_Url__c = envelopeUri;
|
|
||||||
}
|
|
||||||
|
|
||||||
update updateCase;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static String formatAddress(Appraiser_Case__c appraiserCase) {
|
private static String formatAddress(Appraiser_Case__c appraiserCase) {
|
||||||
return AppraiserCasePayloadBuilder.formatMailingAddress(
|
return AppraiserCasePayloadBuilder.formatMailingAddress(
|
||||||
appraiserCase.Property_Street__c,
|
appraiserCase.Property_Street__c,
|
||||||
|
|
|
||||||
|
|
@ -43,14 +43,6 @@ public with sharing class DocusignESignatureService {
|
||||||
@AuraEnabled public String rawJson;
|
@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)
|
@AuraEnabled(cacheable=true)
|
||||||
public static ESignatureAccountConfig getAccountConfig(String accountCode) {
|
public static ESignatureAccountConfig getAccountConfig(String accountCode) {
|
||||||
CLM_Account_Setting__mdt row = requireAccountSetting(accountCode);
|
CLM_Account_Setting__mdt row = requireAccountSetting(accountCode);
|
||||||
|
|
@ -152,78 +144,6 @@ public with sharing class DocusignESignatureService {
|
||||||
return parseEnvelopeList(response.responseBody);
|
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<String, Object> roleMap = new Map<String, Object>{
|
|
||||||
'email' => caseRecord.Appraiser_Email__c,
|
|
||||||
'name' => appraiserName,
|
|
||||||
'roleName' => 'Signer'
|
|
||||||
};
|
|
||||||
Map<String, Object> bodyMap = new Map<String, Object>{
|
|
||||||
'templateId' => templateId,
|
|
||||||
'templateRoles' => new List<Object>{ 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<String, Object> responseMap = (Map<String, Object>) 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
|
@TestVisible
|
||||||
private static List<ESignatureAccountSummary> parseAccountList(String body) {
|
private static List<ESignatureAccountSummary> parseAccountList(String body) {
|
||||||
Object root = JSON.deserializeUntyped(body);
|
Object root = JSON.deserializeUntyped(body);
|
||||||
|
|
|
||||||
|
|
@ -147,92 +147,4 @@ private class DocusignESignatureServiceTest {
|
||||||
DocusignESignatureService.buildEndpoint('/v2.1/accounts', 'Esignature_Demo_NamedCreds')
|
DocusignESignatureService.buildEndpoint('/v2.1/accounts', 'Esignature_Demo_NamedCreds')
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private class CreateEnvelopeSuccessMock implements HttpCalloutMock {
|
|
||||||
public HttpResponse respond(HttpRequest req) {
|
|
||||||
HttpResponse res = new HttpResponse();
|
|
||||||
res.setHeader('Content-Type', 'application/json');
|
|
||||||
res.setStatusCode(201);
|
|
||||||
res.setBody('{"envelopeId":"abc-123","status":"sent","uri":"/v2.1/accounts/12345678/envelopes/abc-123"}');
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private class CreateEnvelopeFailureMock implements HttpCalloutMock {
|
|
||||||
public HttpResponse respond(HttpRequest req) {
|
|
||||||
HttpResponse res = new HttpResponse();
|
|
||||||
res.setHeader('Content-Type', 'application/json');
|
|
||||||
res.setStatusCode(400);
|
|
||||||
res.setBody('{"errorCode":"TEMPLATE_NOT_IN_ACCOUNT","message":"The template specified is not in the account."}');
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@IsTest
|
|
||||||
static void createEnvelopeReturnsSuccessResultOnHttp201() {
|
|
||||||
Appraiser_Case__c appraiserCase = new Appraiser_Case__c(
|
|
||||||
Appraiser_Field_Review_Date__c = Date.today(),
|
|
||||||
Appraiser_Email__c = 'appraiser@example.com',
|
|
||||||
Appraiser_Name__c = 'Jamie Carter'
|
|
||||||
);
|
|
||||||
insert appraiserCase;
|
|
||||||
|
|
||||||
Test.setMock(HttpCalloutMock.class, new CreateEnvelopeSuccessMock());
|
|
||||||
|
|
||||||
Test.startTest();
|
|
||||||
DocusignESignatureService.EnvelopeCreateResult result = DocusignESignatureService.createEnvelope(
|
|
||||||
appraiserCase.Id,
|
|
||||||
'DTC_CLM_Demo',
|
|
||||||
'tmpl-001'
|
|
||||||
);
|
|
||||||
Test.stopTest();
|
|
||||||
|
|
||||||
System.assertEquals(true, result.success);
|
|
||||||
System.assertEquals('abc-123', result.envelopeId);
|
|
||||||
System.assertEquals('sent', result.status);
|
|
||||||
}
|
|
||||||
|
|
||||||
@IsTest
|
|
||||||
static void createEnvelopeReturnsFailureResultOnHttp400() {
|
|
||||||
Appraiser_Case__c appraiserCase = new Appraiser_Case__c(
|
|
||||||
Appraiser_Field_Review_Date__c = Date.today(),
|
|
||||||
Appraiser_Email__c = 'appraiser@example.com',
|
|
||||||
Appraiser_Name__c = 'Jamie Carter'
|
|
||||||
);
|
|
||||||
insert appraiserCase;
|
|
||||||
|
|
||||||
Test.setMock(HttpCalloutMock.class, new CreateEnvelopeFailureMock());
|
|
||||||
|
|
||||||
Test.startTest();
|
|
||||||
DocusignESignatureService.EnvelopeCreateResult result = DocusignESignatureService.createEnvelope(
|
|
||||||
appraiserCase.Id,
|
|
||||||
'DTC_CLM_Demo',
|
|
||||||
'tmpl-bad'
|
|
||||||
);
|
|
||||||
Test.stopTest();
|
|
||||||
|
|
||||||
System.assertEquals(false, result.success);
|
|
||||||
System.assertNotEquals(null, result.errorMessage);
|
|
||||||
System.assert(result.errorMessage.contains('400'));
|
|
||||||
}
|
|
||||||
|
|
||||||
@IsTest
|
|
||||||
static void createEnvelopeReturnsEarlyWhenEmailIsBlank() {
|
|
||||||
Appraiser_Case__c appraiserCase = new Appraiser_Case__c(
|
|
||||||
Appraiser_Field_Review_Date__c = Date.today()
|
|
||||||
);
|
|
||||||
insert appraiserCase;
|
|
||||||
|
|
||||||
Test.startTest();
|
|
||||||
DocusignESignatureService.EnvelopeCreateResult result = DocusignESignatureService.createEnvelope(
|
|
||||||
appraiserCase.Id,
|
|
||||||
'DTC_CLM_Demo',
|
|
||||||
'tmpl-001'
|
|
||||||
);
|
|
||||||
Test.stopTest();
|
|
||||||
|
|
||||||
System.assertEquals(false, result.success);
|
|
||||||
System.assertNotEquals(null, result.errorMessage);
|
|
||||||
System.assert(result.errorMessage.toLowerCase().contains('email'));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,8 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<CustomField xmlns="http://soap.sforce.com/2006/04/metadata">
|
|
||||||
<fullName>ESignature_Completed_At__c</fullName>
|
|
||||||
<label>eSignature Completed At</label>
|
|
||||||
<required>false</required>
|
|
||||||
<trackHistory>false</trackHistory>
|
|
||||||
<type>DateTime</type>
|
|
||||||
</CustomField>
|
|
||||||
|
|
@ -1,11 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<CustomField xmlns="http://soap.sforce.com/2006/04/metadata">
|
|
||||||
<fullName>ESignature_Envelope_Id__c</fullName>
|
|
||||||
<externalId>false</externalId>
|
|
||||||
<label>eSignature Envelope ID</label>
|
|
||||||
<length>255</length>
|
|
||||||
<required>false</required>
|
|
||||||
<trackHistory>false</trackHistory>
|
|
||||||
<type>Text</type>
|
|
||||||
<unique>false</unique>
|
|
||||||
</CustomField>
|
|
||||||
|
|
@ -1,11 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<CustomField xmlns="http://soap.sforce.com/2006/04/metadata">
|
|
||||||
<fullName>ESignature_Envelope_Status__c</fullName>
|
|
||||||
<externalId>false</externalId>
|
|
||||||
<label>eSignature Envelope Status</label>
|
|
||||||
<length>50</length>
|
|
||||||
<required>false</required>
|
|
||||||
<trackHistory>false</trackHistory>
|
|
||||||
<type>Text</type>
|
|
||||||
<unique>false</unique>
|
|
||||||
</CustomField>
|
|
||||||
|
|
@ -1,9 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<CustomField xmlns="http://soap.sforce.com/2006/04/metadata">
|
|
||||||
<fullName>ESignature_Envelope_Url__c</fullName>
|
|
||||||
<externalId>false</externalId>
|
|
||||||
<label>eSignature Envelope URL</label>
|
|
||||||
<required>false</required>
|
|
||||||
<trackHistory>false</trackHistory>
|
|
||||||
<type>Url</type>
|
|
||||||
</CustomField>
|
|
||||||
|
|
@ -1,8 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<CustomField xmlns="http://soap.sforce.com/2006/04/metadata">
|
|
||||||
<fullName>ESignature_Sent_At__c</fullName>
|
|
||||||
<label>eSignature Sent At</label>
|
|
||||||
<required>false</required>
|
|
||||||
<trackHistory>false</trackHistory>
|
|
||||||
<type>DateTime</type>
|
|
||||||
</CustomField>
|
|
||||||
|
|
@ -491,7 +491,8 @@ for i in $(seq 1 "$MAX_ITERATIONS"); do
|
||||||
run_agent "$i" build
|
run_agent "$i" build
|
||||||
logfile="$LOG_DIR/iteration-${i}.log"
|
logfile="$LOG_DIR/iteration-${i}.log"
|
||||||
|
|
||||||
status=0; check_output "$logfile" || status=$?
|
check_output "$logfile"
|
||||||
|
status=$?
|
||||||
|
|
||||||
case $status in
|
case $status in
|
||||||
0)
|
0)
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue