From ce1a5aa63e7b461bf37049317ed7053825285ad1 Mon Sep 17 00:00:00 2001 From: Luke Parrott Date: Tue, 9 Aug 2022 13:20:23 -0500 Subject: [PATCH 001/445] Bug fix for syncing Allocations when Opportunity is updated to $0 Amount --- .../default/classes/ALLO_Allocations_TDTM.cls | 12 +++- .../default/classes/ALLO_Allocations_TEST.cls | 59 ++++++++++++++++++- 2 files changed, 68 insertions(+), 3 deletions(-) diff --git a/force-app/main/default/classes/ALLO_Allocations_TDTM.cls b/force-app/main/default/classes/ALLO_Allocations_TDTM.cls index fa93d4978d1..40f976e04c8 100644 --- a/force-app/main/default/classes/ALLO_Allocations_TDTM.cls +++ b/force-app/main/default/classes/ALLO_Allocations_TDTM.cls @@ -747,8 +747,18 @@ public class ALLO_Allocations_TDTM extends TDTM_Runnable { } else { for (Allocation__c allo : oppWrap.listAllo) { + // if the Opportunity is for $0, we can safely update fixed Allocations to be $0 + // This fixes a bug with Elevate refunds when non-default Allocations have fixed (not percent based) Amounts + if (oppWrap.parentAmount == 0 && allo.Percent__c == null) { + oppWrap.totalAmount -= allo.Amount__c; + allo.Amount__c = 0; + + if (UserInfo.isMultiCurrencyOrganization()) { + allo.put('CurrencyIsoCode', opp.get('CurrencyIsoCode')); + } + dmlWrapper.objectsToUpdate.add(allo); //if the percentage changed, recalculate the amount - if (allo.Percent__c!=null && allo.Percent__c>0 && allo.Amount__c != (oppWrap.parentAmount * allo.Percent__c * .01).setScale(2)) { + } else if (allo.Percent__c != null && allo.Percent__c > 0 && allo.Amount__c != (oppWrap.parentAmount * allo.Percent__c * .01).setScale(2)) { //remove the previous amount, recalculate the amount, and add it back oppWrap.totalAmount -= allo.Amount__c; allo.Amount__c = (oppWrap.parentAmount * allo.Percent__c * .01).setScale(2); diff --git a/force-app/main/default/classes/ALLO_Allocations_TEST.cls b/force-app/main/default/classes/ALLO_Allocations_TEST.cls index c85f072d966..8a7d9c95772 100644 --- a/force-app/main/default/classes/ALLO_Allocations_TEST.cls +++ b/force-app/main/default/classes/ALLO_Allocations_TEST.cls @@ -1393,7 +1393,7 @@ private with sharing class ALLO_Allocations_TEST { createOppAllocation(gau1.Id, opps[0].Id, null, 50) ); - // Fix Amount Allocation for $0 to GAU 2 + // Fix Amount Allocation for $10 to GAU 2 newAllos.add( createOppAllocation(gau2.Id, opps[0].Id, 10, null) ); @@ -1402,7 +1402,62 @@ private with sharing class ALLO_Allocations_TEST { insert newAllos; System.assert(false, 'Expected an exception due to Opportunity being Overallocated.'); } catch (Exception e) { - System.assert(e.getMessage().contains(Label.alloTotalExceedsOppAmt), 'Expected Exception Text: ' + Label.alloExceedsOppAmount + '; Actual: ' + e.getMessage()); + System.assert(e.getMessage().contains(Label.alloTotalExceedsOppAmt), 'Expected Exception Text: ' + Label.alloTotalExceedsOppAmt + '; Actual: ' + e.getMessage()); + } + + Test.stopTest(); + } + + /******************************************************************************************************* + * @description Tests that an error is generated if Opportunity Amount is 0 and a non zero fixed + * Amount Allocation is added + ********************************************************************************************************/ + @isTest + private static void testFixupForNonZeroFixedAmountAllocationIfOpportunityAmountIsZero() { + List gaus = new List(); + + General_Accounting_Unit__c defaultGau = new General_Accounting_Unit__c(Name='General'); + gaus.add(defaultGAU); + General_Accounting_Unit__c gau1 = new General_Accounting_Unit__c(Name = 'GAU 1'); + gaus.add(gau1); + + insert gaus; + + setupSettings(new Allocations_Settings__c(Default_Allocations_Enabled__c = true, Default__c = defaultGau.Id)); + + List accs = UTIL_UnitTestData_TEST.createMultipleTestAccounts(1, null); + + insert accs; + + List opps = UTIL_UnitTestData_TEST.oppsForAccountList(accs, null, UTIL_UnitTestData_TEST.getClosedWonStage(), System.today(), 10, null, null); + + insert opps; + + Test.startTest(); + + List newAllos = new List(); + + // Fix Amount Allocation for $10 to GAU 1 + newAllos.add( + createOppAllocation(gau1.Id, opps[0].Id, 10, null) + ); + + insert newAllos; + + try { + opps[0].Amount = 5; + update opps[0]; + System.assert(false, 'Expected an exception due to Opportunity being Overallocated.'); + } catch (Exception e) { + System.assert(e.getMessage().contains(Label.alloExceedsOppAmount), 'Expected Exception Text: ' + Label.alloExceedsOppAmount + '; Actual: ' + e.getMessage()); + } + + try { + opps[0].Amount = 0; + update opps[0]; + System.assert(true, 'Expected no Exception since Opportunity had an Amount of 0'); + } catch (Exception e) { + System.assert(false, 'Expected no Exception: ' + e.getMessage()); } Test.stopTest(); From 221f6274333cb1dba36bbef67de6bdc55dcbc90c Mon Sep 17 00:00:00 2001 From: Luke Parrott Date: Tue, 9 Aug 2022 13:56:09 -0500 Subject: [PATCH 002/445] Update test description --- force-app/main/default/classes/ALLO_Allocations_TEST.cls | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/force-app/main/default/classes/ALLO_Allocations_TEST.cls b/force-app/main/default/classes/ALLO_Allocations_TEST.cls index 8a7d9c95772..89994b51e8d 100644 --- a/force-app/main/default/classes/ALLO_Allocations_TEST.cls +++ b/force-app/main/default/classes/ALLO_Allocations_TEST.cls @@ -1409,8 +1409,8 @@ private with sharing class ALLO_Allocations_TEST { } /******************************************************************************************************* - * @description Tests that an error is generated if Opportunity Amount is 0 and a non zero fixed - * Amount Allocation is added + * @description Tests that no error is generated if Opportunity Amount becomes 0 and a non zero fixed + * Amount Allocation exists ********************************************************************************************************/ @isTest private static void testFixupForNonZeroFixedAmountAllocationIfOpportunityAmountIsZero() { From b9e6ad8f4c0f29baefccea47ae3912cde7ef2199 Mon Sep 17 00:00:00 2001 From: Luke Parrott Date: Tue, 16 Aug 2022 09:59:34 -0500 Subject: [PATCH 003/445] Updating Allocation unit test to have clear assertions --- .../default/classes/ALLO_Allocations_TEST.cls | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/force-app/main/default/classes/ALLO_Allocations_TEST.cls b/force-app/main/default/classes/ALLO_Allocations_TEST.cls index 89994b51e8d..850a75008c3 100644 --- a/force-app/main/default/classes/ALLO_Allocations_TEST.cls +++ b/force-app/main/default/classes/ALLO_Allocations_TEST.cls @@ -1432,6 +1432,7 @@ private with sharing class ALLO_Allocations_TEST { List opps = UTIL_UnitTestData_TEST.oppsForAccountList(accs, null, UTIL_UnitTestData_TEST.getClosedWonStage(), System.today(), 10, null, null); insert opps; + Opportunity opportunity = opps[0]; Test.startTest(); @@ -1439,25 +1440,24 @@ private with sharing class ALLO_Allocations_TEST { // Fix Amount Allocation for $10 to GAU 1 newAllos.add( - createOppAllocation(gau1.Id, opps[0].Id, 10, null) + createOppAllocation(gau1.Id, opportunity.Id, 10, null) ); insert newAllos; try { - opps[0].Amount = 5; - update opps[0]; + opportunity.Amount = 5; + update opportunity; System.assert(false, 'Expected an exception due to Opportunity being Overallocated.'); } catch (Exception e) { System.assert(e.getMessage().contains(Label.alloExceedsOppAmount), 'Expected Exception Text: ' + Label.alloExceedsOppAmount + '; Actual: ' + e.getMessage()); } - try { - opps[0].Amount = 0; - update opps[0]; - System.assert(true, 'Expected no Exception since Opportunity had an Amount of 0'); - } catch (Exception e) { - System.assert(false, 'Expected no Exception: ' + e.getMessage()); + opportunity.Amount = 0; + update opportunity; + Map oppAllos = getOpportunityAllocationsByGAU(opportunity.Id); + for(Id gauId : oppAllos.keySet()) { + System.assertEquals(0, oppAllos.get(gauId).Amount__c, 'Allocaiton Amount should be 0'); } Test.stopTest(); From 1e506262e9b717f6fb63b0917e0f63ace57fac5e Mon Sep 17 00:00:00 2001 From: Victor Oduyemi Date: Tue, 16 Aug 2022 11:15:09 -0600 Subject: [PATCH 004/445] Clarifying refactors for the PauseForm Controller --- .../default/classes/RD2_PauseForm_CTRL.cls | 52 ++++++++++++------- .../default/classes/RD2_ScheduleService.cls | 1 - 2 files changed, 33 insertions(+), 20 deletions(-) diff --git a/force-app/main/default/classes/RD2_PauseForm_CTRL.cls b/force-app/main/default/classes/RD2_PauseForm_CTRL.cls index b8414c80a29..4d40bc6e20d 100644 --- a/force-app/main/default/classes/RD2_PauseForm_CTRL.cls +++ b/force-app/main/default/classes/RD2_PauseForm_CTRL.cls @@ -187,28 +187,42 @@ public with sharing class RD2_PauseForm_CTRL { try { PauseData pause = (PauseData) JSON.deserialize(jsonPauseData, PauseData.class); - RD2_RecurringDonation rd = getRecurringDonation(pause.rdId); - if (RD2_ElevateIntegrationService.isIntegrationEnabled() && rd.isElevateRecord()) { - throw new PauseException(System.Label.RD2_ElevateNotSupported); - } - - Boolean isNewPause = pause.startDate != null && pause.resumeAfterDate != null; - if (isNewPause) { - //deactivate the current pause (if any) and create a new one - pauseHandler.savePauseSchedule(pause.rdId, buildPauseSchedule(pause)); + pauseElevateRDsFor(pause); + processPauseScheduleFor(pause); - } else {//deactivate the current pause (if any) - pauseHandler.cancelPauseSchedule(pause.rdId); - } - - Boolean isScheduleChanged = true; - RD2_QueueableService.enqueueOppEvalService(pause.rdId, isScheduleChanged); + RD2_QueueableService.enqueueOppEvalService(pause.rdId, true); } catch (Exception e) { throwAuraHandledException(e.getMessage()); } } + @TestVisible + private static void processPauseScheduleFor(PauseData pause) { + + pauseHandler.cancelPauseSchedule(pause.rdId); + + if (isNewPause(pause)) { + pauseHandler.savePauseSchedule(pause.rdId, buildPauseSchedule(pause)); + } + + } + + private static void pauseElevateRDsFor(PauseData pause) { + RD2_RecurringDonation rd = getRecurringDonation(pause.rdId); + if (isOnlineRecurringDonationRecord(rd)) { + // branch out to make call out to elevate here + } + } + + private static Boolean isOnlineRecurringDonationRecord(RD2_RecurringDonation rd) { + return RD2_ElevateIntegrationService.isIntegrationEnabled() && rd.isElevateRecord(); + } + + private static Boolean isNewPause(PauseData pause) { + return pause.startDate != null && pause.resumeAfterDate != null; + } + /** * @description Constructs schedule to be inserted for the new pause * @param pause PauseData @@ -216,10 +230,10 @@ public with sharing class RD2_PauseForm_CTRL { */ private static RecurringDonationSchedule__c buildPauseSchedule(PauseData pause) { return pauseHandler.createPauseSchedule( - pause.pausedReason?.value, - pause.startDate, - pause.resumeAfterDate, - pause.rdId + pause.pausedReason?.value, + pause.startDate, + pause.resumeAfterDate, + pause.rdId ); } diff --git a/force-app/main/default/classes/RD2_ScheduleService.cls b/force-app/main/default/classes/RD2_ScheduleService.cls index e3eebd67f32..1ebf1ae9083 100644 --- a/force-app/main/default/classes/RD2_ScheduleService.cls +++ b/force-app/main/default/classes/RD2_ScheduleService.cls @@ -1120,7 +1120,6 @@ public without sharing class RD2_ScheduleService { } catch (Exception e) { Database.rollback(sp); - throw new ScheduleException(e.getMessage()); } } From 9f26528dd0ac1b7e7fb6ded6c7cbbc844b2d649f Mon Sep 17 00:00:00 2001 From: screcco-sfdo Date: Tue, 16 Aug 2022 16:42:01 -0400 Subject: [PATCH 005/445] Add inherity sharing to inner classes --- force-app/main/default/classes/GiftBatchService.cls | 2 +- force-app/main/default/classes/PMT_RefundController.cls | 2 +- force-app/main/default/classes/PMT_RefundService.cls | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/force-app/main/default/classes/GiftBatchService.cls b/force-app/main/default/classes/GiftBatchService.cls index 427382b4355..e42bc4282aa 100644 --- a/force-app/main/default/classes/GiftBatchService.cls +++ b/force-app/main/default/classes/GiftBatchService.cls @@ -165,7 +165,7 @@ public with sharing class GiftBatchService { } } - private class GiftBatchServicePrivilegedHelper { + private inherited sharing class GiftBatchServicePrivilegedHelper { private DataImportBatch__c batch; private AsyncApexJob asyncApexJob; private final String JOB_TYPE_BATCH = 'BatchApex'; diff --git a/force-app/main/default/classes/PMT_RefundController.cls b/force-app/main/default/classes/PMT_RefundController.cls index de33ac7e665..19273c4e77a 100644 --- a/force-app/main/default/classes/PMT_RefundController.cls +++ b/force-app/main/default/classes/PMT_RefundController.cls @@ -167,7 +167,7 @@ public with sharing class PMT_RefundController { } } - public class RefundView { + public inherited sharing class RefundView { @AuraEnabled public Boolean hasRequiredPermissions; @AuraEnabled public Boolean isSuccess; @AuraEnabled public Id redirectToPaymentId; diff --git a/force-app/main/default/classes/PMT_RefundService.cls b/force-app/main/default/classes/PMT_RefundService.cls index 2bfde437841..5bec0a40915 100644 --- a/force-app/main/default/classes/PMT_RefundService.cls +++ b/force-app/main/default/classes/PMT_RefundService.cls @@ -292,7 +292,7 @@ public inherited sharing class PMT_RefundService { } } - public class RequestBody { + public inherited sharing class RequestBody { @TestVisible String transactionId; @@ -301,7 +301,7 @@ public inherited sharing class PMT_RefundService { } } - public class RefundInfo { + public inherited sharing class RefundInfo { public npe01__OppPayment__c originalPayment; public Decimal remainingBalance; From 6ebfa99718fc3492ad52e3714e008fc9d20d3836 Mon Sep 17 00:00:00 2001 From: Victor Oduyemi Date: Wed, 17 Aug 2022 10:54:19 -0600 Subject: [PATCH 006/445] Added API call for online RD pause --- .../default/classes/PS_CommitmentRequest.cls | 37 ++- .../classes/PS_CommitmentRequest_TEST.cls | 6 +- force-app/main/default/classes/PS_Request.cls | 10 +- .../default/classes/RD2_CommitmentService.cls | 216 ++++++++++++++++++ .../RD2_CommitmentService.cls-meta.xml | 5 + .../classes/RD2_EntryFormController.cls | 183 +-------------- .../default/classes/RD2_PauseForm_CTRL.cls | 30 ++- .../default/classes/RD2_RecurringDonation.cls | 4 + .../default/classes/RD2_ScheduleService.cls | 3 - 9 files changed, 302 insertions(+), 192 deletions(-) create mode 100644 force-app/main/default/classes/RD2_CommitmentService.cls create mode 100644 force-app/main/default/classes/RD2_CommitmentService.cls-meta.xml diff --git a/force-app/main/default/classes/PS_CommitmentRequest.cls b/force-app/main/default/classes/PS_CommitmentRequest.cls index 30f7a5b14a3..b844a63c405 100644 --- a/force-app/main/default/classes/PS_CommitmentRequest.cls +++ b/force-app/main/default/classes/PS_CommitmentRequest.cls @@ -168,14 +168,15 @@ public inherited sharing class PS_CommitmentRequest { * @param jsonRequestBody JSON containing parameters for the purchase call request body * @return HttpRequest */ - public static HttpRequest buildRequest(String commitmentId, String jsonRequestBody) { + public static HttpRequest buildRequest(String commitmentId, String jsonRequestBody, + PS_Request.ElevateEndpoint endpoint) { UTIL_Http.Method method = String.isBlank(commitmentId) ? UTIL_Http.Method.POST : UTIL_Http.Method.PATCH; return new PS_Request.Builder() .withMethod(method) - .withEndpoint(PS_Request.ElevateEndpoint.COMMITMENT) + .withEndpoint(endpoint) .withRecommendedTimeout() .withBody(jsonRequestBody) .build(); @@ -209,6 +210,13 @@ public inherited sharing class PS_CommitmentRequest { : getUpdateRequestBody(rd, oldRd, token); } + public RequestBody getPauseRequestBody(RecurringDonationSchedule__c schedule) { + return new RequestBody() + .withStartTimestamp(schedule.StartDate__c) + .withEndTimestamp(schedule.EndDate__c) + .withReason(schedule.StatusReason__c); + } + /*** * @description Constructs the create commitment request body for the specified Recurring Donation. * @param rd Recurring Donation record @@ -471,6 +479,11 @@ public inherited sharing class PS_CommitmentRequest { */ public Map productMetadata; + public Datetime startTimestamp; + public Datetime endTimestamp; + public String reason; + public String reasonComment; + /*** * @description Constructor */ @@ -656,6 +669,26 @@ public inherited sharing class PS_CommitmentRequest { public Boolean isCommitmentUpdate() { return this.id != null; } + + public RequestBody withStartTimestamp(Datetime startTimestamp) { + this.startTimestamp = startTimestamp; + return this; + } + + public RequestBody withEndTimestamp(Datetime endTimestamp) { + this.endTimestamp = endTimestamp; + return this; + } + + public RequestBody withReason(String reason) { + this.reason = reason; + return this; + } + + public RequestBody withReasonComment(String reasonComment) { + this.reasonComment = reasonComment; + return this; + } } /*** diff --git a/force-app/main/default/classes/PS_CommitmentRequest_TEST.cls b/force-app/main/default/classes/PS_CommitmentRequest_TEST.cls index c7781afc1f1..6770545cd98 100644 --- a/force-app/main/default/classes/PS_CommitmentRequest_TEST.cls +++ b/force-app/main/default/classes/PS_CommitmentRequest_TEST.cls @@ -306,7 +306,8 @@ private with sharing class PS_CommitmentRequest_TEST { Test.startTest(); String commitmentId = null; - HttpRequest request = PS_CommitmentRequest.buildRequest(commitmentId, jsonRequestBody); + HttpRequest request = PS_CommitmentRequest.buildRequest(commitmentId, jsonRequestBody, + PS_Request.ElevateEndpoint.COMMITMENT); Test.stopTest(); final String expectedEndpoint = PS_IntegrationServiceConfig_TEST.testBaseUrl @@ -327,7 +328,8 @@ private with sharing class PS_CommitmentRequest_TEST { Test.startTest(); String commitmentId = RD2_ElevateIntegrationService_TEST.COMMITMENT_ID; - HttpRequest request = PS_CommitmentRequest.buildRequest(commitmentId, jsonRequestBody); + HttpRequest request = PS_CommitmentRequest.buildRequest(commitmentId, jsonRequestBody, + PS_Request.ElevateEndpoint.COMMITMENT); Test.stopTest(); final String expectedEndpoint = PS_IntegrationServiceConfig_TEST.testBaseUrl diff --git a/force-app/main/default/classes/PS_Request.cls b/force-app/main/default/classes/PS_Request.cls index b5cf70e1009..93874e2e449 100644 --- a/force-app/main/default/classes/PS_Request.cls +++ b/force-app/main/default/classes/PS_Request.cls @@ -40,6 +40,7 @@ public inherited sharing class PS_Request { PURCHASE, REFUND, COMMITMENT, + COMMITMENT_PAUSE, COMMITMENT_CANCEL, COMMITMENT_UPDATE_BULK, CREATE_ELEVATE_BATCH, @@ -83,6 +84,7 @@ public inherited sharing class PS_Request { public static String ENDPOINT_ADD_TO_ELEVATE_BATCH = '/v1/payments/verified/batch/{0}/add'; public static String ENDPOINT_CHARGE_ELEVATE_BATCH = '/v1/payments/verified/batch/{0}/capture'; public static String ENDPOINT_REMOVE_FROM_ELEVATE_BATCH = '/v1/payments/verified/batch/{0}/remove/{1}'; + public static String ENDPOINT_COMMITMENT_PAUSE = '/v1/payments/verified/commitments/{0}/pause'; public static String PRODUCT_METADATA_SCHEMA_URI = 'https://payments-js.elevate.salesforce.org/schema/productMetadata/donation-v1.1.0'; private static Integer RECOMMENDED_TIMEOUT_MS = 20000; @@ -258,7 +260,12 @@ public inherited sharing class PS_Request { if (String.isNotBlank(commitmentId)) { value += '/' + commitmentId; } + } else if (endpoint == ElevateEndpoint.COMMITMENT_PAUSE) { + value = ENDPOINT_COMMITMENT_PAUSE; + if (String.isNotBlank(commitmentId)) { + value += '/' + commitmentId; + } } else if (endpoint == ElevateEndpoint.COMMITMENT_CANCEL) { value = String.format( ENDPOINT_COMMITMENT_CANCEL, @@ -425,7 +432,8 @@ public inherited sharing class PS_Request { private Boolean isCommitmentEndpoint () { return endpoint == ElevateEndpoint.COMMITMENT || endpoint == ElevateEndpoint.COMMITMENT_CANCEL - || endpoint == ElevateEndpoint.COMMITMENT_UPDATE_BULK; + || endpoint == ElevateEndpoint.COMMITMENT_UPDATE_BULK + || endpoint == ElevateEndpoint.COMMITMENT_PAUSE; } private Boolean isRefundEndpoint() { diff --git a/force-app/main/default/classes/RD2_CommitmentService.cls b/force-app/main/default/classes/RD2_CommitmentService.cls new file mode 100644 index 00000000000..1fd290cc7d8 --- /dev/null +++ b/force-app/main/default/classes/RD2_CommitmentService.cls @@ -0,0 +1,216 @@ +/** + * Created by voduyemi on 8/17/22. + */ + +/** +* @description Handles sending commitment create and edit requests +*/ +public without sharing class RD2_CommitmentService { + + /** + * @description Sends commitment create/update requests, and constructs a response + */ + private UTIL_Http.RequestService requestService { + get { + if (requestService == null) { + requestService = new UTIL_Http.RequestService(); + } + return requestService; + } + set; + } + + /** + * Used to adjust and validate Recurring Donation data + */ + private RD2_DataRegulationService dataService { + get { + if (dataService == null) { + dataService = new RD2_DataRegulationService(); + } + return dataService; + } + set; + } + + /** + * @description Handles validation and Elevate recurring commitment creation + * @param rd Recurring Donation + * @param oldRd oldRecurring Donation + * @param paymentMethodToken Payment Method Token + * @return UTIL_Http.Response Payments API response + */ + public UTIL_Http.Response handleCommitment(npe03__Recurring_Donation__c rd, npe03__Recurring_Donation__c oldRd, String paymentMethodToken) { + UTIL_Http.Response response; + + if (shouldSendToElevate(rd, oldRd, paymentMethodToken)) { + PS_CommitmentRequest.RequestBody requestBody = new PS_CommitmentRequest().getRequestBody(rd, oldRd, paymentMethodToken); + + response = sendRequest(rd.CommitmentId__c, JSON.serialize(requestBody)); + + processResponse(rd, response); + } + + return response; + } + + public UTIL_Http.Response handleCommitmentPause(RecurringDonationSchedule__c schedule, RD2_RecurringDonation rd) { + UTIL_Http.Response response; + + PS_CommitmentRequest.RequestBody requestBody = new PS_CommitmentRequest().getPauseRequestBody(schedule); + + response = sendRequest(rd.getRecord().CommitmentId__c, JSON.serialize(requestBody)); + + processResponse(rd, response); + + return response; + } + + /** + * @description Constructs Recurring Donation record from received fields specified in the JSON string, + * updates defaults that are otherwise updated in the trigger context, and + * validates user entered values for a new or existing RD record. + * @param rd Modified Recurring Donation record that is not created/updated in DB yet + * @param oldRd oldRecurring Donation + */ + public void adjustAndValidateRD(npe03__Recurring_Donation__c rd, npe03__Recurring_Donation__c oldRd) { + // Populate defaults otherwise applied by the DML operation and available in the trigger context + if (String.isBlank(rd.Status__c)) { + rd.Status__c = UTIL_Describe.getDefaultSelectOption( + 'npe03__Recurring_Donation__c', String.valueOf(npe03__Recurring_Donation__c.Status__c) + ); + } + + List newRds = new List{rd}; + List oldRds = new List(); + if (rd.Id != null) { + oldRds.add(oldRd); + } + + RD2_DataRegulationService regulationService = new RD2_DataRegulationService(); + regulationService.adjust(newRds, oldRds); + regulationService.markRDsAsElevate(newRds); + + List errorRds = new RD2_ValidationService(newRds, oldRds) + .validate(); + + regulationService.removeElevateMarker(newRds); + + if (!errorRds.isEmpty()) { + UTIL_AuraEnabledCommon.throwAuraHandledException( + errorRds[0].getFirstError() + ); + } + } + + /** + * @description Checks if the commitment record should be sent to Elevate + * @param rd Recurring Donation record + * @param oldRd oldRecurring Donation + * @param paymentMethodToken Token for Elevate requests + * @return Boolean + */ + private Boolean shouldSendToElevate(npe03__Recurring_Donation__c rd, npe03__Recurring_Donation__c oldRd, String paymentMethodToken) { + if(new RD2_RecurringDonation(rd).isClosed()) { + return false; + } + PS_CommitmentRequest request = new PS_CommitmentRequest(); + Boolean isElevatedFieldsChanged = request.isElevateScheduleFieldsChanged(rd, oldRd) + || request.isElevateCampaignChanged(rd, oldRd); + + + return rd.Id == null + || (isElevatedFieldsChanged && rd.CommitmentId__c != null) + || String.isNotBlank(paymentMethodToken); + } + + /** + * @description Sends commitment request to Elevate + * @param commitmentId Elevate recurring commitment Id + * @param jsonRequestBody Payment API request in JSON format + * @return response Payments API response + */ + private UTIL_Http.Response sendRequest(String commitmentId, String jsonRequestBody) { + UTIL_Http.Response response; + + try { + HttpRequest request = PS_CommitmentRequest.buildRequest(commitmentId, jsonRequestBody, + PS_Request.ElevateEndpoint.COMMITMENT); + + response = requestService.sendRequest(request); + + } catch (Exception ex) { + response = requestService.buildErrorResponse(ex); + } + + return response; + } + + /** + * @description Logs an error record on an commitment error response + * @param rd Recurring Donation record + * @param response Payments API response + */ + private void processResponse(npe03__Recurring_Donation__c rd, UTIL_Http.Response response) { + if (isCommitmentSuccess(response)) { + return; + } + + Id recordId = rd.Id != null + ? rd.Id + : rd.npe03__Contact__c != null + ? rd.npe03__Contact__c + : rd.npe03__Organization__c; + + logError(recordId, response.getErrorMessages()); + } + + /** +* @description Logs an error record on an commitment error response +* @param rd Recurring Donation record +* @param response Payments API response +*/ + private void processResponse(RD2_RecurringDonation donation, UTIL_Http.Response response) { + if (isCommitmentSuccess(response)) { + return; + } + npe03__Recurring_Donation__c rd = donation.getRecord(); + Id recordId = rd.Id != null + ? rd.Id + : rd.npe03__Contact__c != null + ? rd.npe03__Contact__c + : rd.npe03__Organization__c; + + logError(recordId, response.getErrorMessages()); + } + + /** + * @description Determines if the commitment has been created or updated successfully + * @param response Payments API response + * @return Boolean + */ + private Boolean isCommitmentSuccess(UTIL_Http.Response response) { + return response.statusCode == UTIL_Http.STATUS_CODE_CREATED + || response.statusCode == UTIL_Http.STATUS_CODE_OK; + } + + /** + * @description Creates an error record for the specified record Id and error message + * @param recordId A Recurring Donation or a donor (Contact/Account) Id + * @param errorMessage Error message + */ + public void logError(Id recordId, String errorMessage) { + ERR_LogService.Logger logger = new ERR_LogService.Logger( + ERR_Handler_API.Context.Elevate, + npe03__Recurring_Donation__c.SObjectType + ); + + String errorType = (recordId.getSobjectType() == Schema.npe03__Recurring_Donation__c.getSObjectType()) + ? RD2_ElevateIntegrationService.LOG_TYPE_COMMITMENT_EDIT + : RD2_ElevateIntegrationService.LOG_TYPE_COMMITMENT_CREATE; + + logger.addError(recordId, errorMessage, errorType); + + logger.processErrors(); + } +} \ No newline at end of file diff --git a/force-app/main/default/classes/RD2_CommitmentService.cls-meta.xml b/force-app/main/default/classes/RD2_CommitmentService.cls-meta.xml new file mode 100644 index 00000000000..4b0bc9f3879 --- /dev/null +++ b/force-app/main/default/classes/RD2_CommitmentService.cls-meta.xml @@ -0,0 +1,5 @@ + + + 55.0 + Active + diff --git a/force-app/main/default/classes/RD2_EntryFormController.cls b/force-app/main/default/classes/RD2_EntryFormController.cls index 2cf84244603..7a701442b15 100644 --- a/force-app/main/default/classes/RD2_EntryFormController.cls +++ b/force-app/main/default/classes/RD2_EntryFormController.cls @@ -39,10 +39,10 @@ public with sharing class RD2_EntryFormController { /** * @description Handles commitment request creation, response parsing and RD Commitment Id update */ - private static CommitmentService commitmentService { + private static RD2_CommitmentService commitmentService { get { if (commitmentService == null) { - commitmentService = new CommitmentService(); + commitmentService = new RD2_CommitmentService(); } return commitmentService; } @@ -334,185 +334,6 @@ public with sharing class RD2_EntryFormController { commitmentService.logError(recordId, errorMessage); } - /** - * @description Handles sending commitment create and edit requests - */ - public without sharing class CommitmentService { - - /** - * @description Sends commitment create/update requests, and constructs a response - */ - private UTIL_Http.RequestService requestService { - get { - if (requestService == null) { - requestService = new UTIL_Http.RequestService(); - } - return requestService; - } - set; - } - - /** - * Used to adjust and validate Recurring Donation data - */ - private RD2_DataRegulationService dataService { - get { - if (dataService == null) { - dataService = new RD2_DataRegulationService(); - } - return dataService; - } - set; - } - - /** - * @description Handles validation and Elevate recurring commitment creation - * @param rd Recurring Donation - * @param oldRd oldRecurring Donation - * @param paymentMethodToken Payment Method Token - * @return UTIL_Http.Response Payments API response - */ - public UTIL_Http.Response handleCommitment(npe03__Recurring_Donation__c rd, npe03__Recurring_Donation__c oldRd, String paymentMethodToken) { - UTIL_Http.Response response; - - if (shouldSendToElevate(rd, oldRd, paymentMethodToken)) { - PS_CommitmentRequest.RequestBody requestBody = new PS_CommitmentRequest().getRequestBody(rd, oldRd, paymentMethodToken); - - response = sendRequest(rd.CommitmentId__c, JSON.serialize(requestBody)); - - processResponse(rd, response); - } - - return response; - } - - /** - * @description Constructs Recurring Donation record from received fields specified in the JSON string, - * updates defaults that are otherwise updated in the trigger context, and - * validates user entered values for a new or existing RD record. - * @param rd Modified Recurring Donation record that is not created/updated in DB yet - * @param oldRd oldRecurring Donation - */ - private void adjustAndValidateRD(npe03__Recurring_Donation__c rd, npe03__Recurring_Donation__c oldRd) { - // Populate defaults otherwise applied by the DML operation and available in the trigger context - if (String.isBlank(rd.Status__c)) { - rd.Status__c = UTIL_Describe.getDefaultSelectOption( - 'npe03__Recurring_Donation__c', String.valueOf(npe03__Recurring_Donation__c.Status__c) - ); - } - - List newRds = new List{rd}; - List oldRds = new List(); - if (rd.Id != null) { - oldRds.add(oldRd); - } - - RD2_DataRegulationService regulationService = new RD2_DataRegulationService(); - regulationService.adjust(newRds, oldRds); - regulationService.markRDsAsElevate(newRds); - - List errorRds = new RD2_ValidationService(newRds, oldRds) - .validate(); - - regulationService.removeElevateMarker(newRds); - - if (!errorRds.isEmpty()) { - UTIL_AuraEnabledCommon.throwAuraHandledException( - errorRds[0].getFirstError() - ); - } - } - - /** - * @description Checks if the commitment record should be sent to Elevate - * @param rd Recurring Donation record - * @param oldRd oldRecurring Donation - * @param paymentMethodToken Token for Elevate requests - * @return Boolean - */ - private Boolean shouldSendToElevate(npe03__Recurring_Donation__c rd, npe03__Recurring_Donation__c oldRd, String paymentMethodToken) { - if(new RD2_RecurringDonation(rd).isClosed()) { - return false; - } - PS_CommitmentRequest request = new PS_CommitmentRequest(); - Boolean isElevatedFieldsChanged = request.isElevateScheduleFieldsChanged(rd, oldRd) - || request.isElevateCampaignChanged(rd, oldRd); - return rd.Id == null - || (isElevatedFieldsChanged && rd.CommitmentId__c != null) - || String.isNotBlank(paymentMethodToken); - } - - /** - * @description Sends commitment request to Elevate - * @param commitmentId Elevate recurring commitment Id - * @param jsonRequestBody Payment API request in JSON format - * @return response Payments API response - */ - private UTIL_Http.Response sendRequest(String commitmentId, String jsonRequestBody) { - UTIL_Http.Response response; - - try { - HttpRequest request = PS_CommitmentRequest.buildRequest(commitmentId, jsonRequestBody); - - response = requestService.sendRequest(request); - - } catch (Exception ex) { - response = requestService.buildErrorResponse(ex); - } - - return response; - } - - /** - * @description Logs an error record on an commitment error response - * @param rd Recurring Donation record - * @param response Payments API response - */ - private void processResponse(npe03__Recurring_Donation__c rd, UTIL_Http.Response response) { - if (isCommitmentSuccess(response)) { - return; - } - - Id recordId = rd.Id != null - ? rd.Id - : rd.npe03__Contact__c != null - ? rd.npe03__Contact__c - : rd.npe03__Organization__c; - - logError(recordId, response.getErrorMessages()); - } - - /** - * @description Determines if the commitment has been created or updated successfully - * @param response Payments API response - * @return Boolean - */ - private Boolean isCommitmentSuccess(UTIL_Http.Response response) { - return response.statusCode == UTIL_Http.STATUS_CODE_CREATED - || response.statusCode == UTIL_Http.STATUS_CODE_OK; - } - - /** - * @description Creates an error record for the specified record Id and error message - * @param recordId A Recurring Donation or a donor (Contact/Account) Id - * @param errorMessage Error message - */ - public void logError(Id recordId, String errorMessage) { - ERR_LogService.Logger logger = new ERR_LogService.Logger( - ERR_Handler_API.Context.Elevate, - npe03__Recurring_Donation__c.SObjectType - ); - - String errorType = (recordId.getSobjectType() == Schema.npe03__Recurring_Donation__c.getSObjectType()) - ? RD2_ElevateIntegrationService.LOG_TYPE_COMMITMENT_EDIT - : RD2_ElevateIntegrationService.LOG_TYPE_COMMITMENT_CREATE; - - logger.addError(recordId, errorMessage, errorType); - - logger.processErrors(); - } - } - } diff --git a/force-app/main/default/classes/RD2_PauseForm_CTRL.cls b/force-app/main/default/classes/RD2_PauseForm_CTRL.cls index 4d40bc6e20d..0d9d1ae753c 100644 --- a/force-app/main/default/classes/RD2_PauseForm_CTRL.cls +++ b/force-app/main/default/classes/RD2_PauseForm_CTRL.cls @@ -48,6 +48,19 @@ public with sharing class RD2_PauseForm_CTRL { } set; } + /** + * @description Handles commitment request creation, response parsing and RD Commitment Id update + */ + private static RD2_CommitmentService commitmentService { + get { + if (commitmentService == null) { + commitmentService = new RD2_CommitmentService(); + } + return commitmentService; + } + set; + } + /** * @description Returns PauseData to the LWC so the page can be initialized and rendered. * Determines if the user has the access to create/update the pause @@ -107,6 +120,8 @@ public with sharing class RD2_PauseForm_CTRL { private static RD2_RecurringDonation getRecurringDonation(Id rdId) { return new RD2_RecurringDonation([ SELECT Status__c, + npe03__Contact__c, + npe03__Organization__c, CommitmentId__c FROM npe03__Recurring_Donation__c WHERE Id = :rdId @@ -208,11 +223,20 @@ public with sharing class RD2_PauseForm_CTRL { } - private static void pauseElevateRDsFor(PauseData pause) { + private static String pauseElevateRDsFor(PauseData pause) { RD2_RecurringDonation rd = getRecurringDonation(pause.rdId); - if (isOnlineRecurringDonationRecord(rd)) { - // branch out to make call out to elevate here + + try { + if (isOnlineRecurringDonationRecord(rd)) { + + UTIL_Http.Response response = commitmentService.handleCommitmentPause(buildPauseSchedule(pause), rd); + return JSON.serialize(response); + } + } catch (Exception ex) { + UTIL_AuraEnabledCommon.throwAuraHandledException(ex.getMessage()); } + + return null; } private static Boolean isOnlineRecurringDonationRecord(RD2_RecurringDonation rd) { diff --git a/force-app/main/default/classes/RD2_RecurringDonation.cls b/force-app/main/default/classes/RD2_RecurringDonation.cls index 49009526ac1..12c4bb25f2f 100755 --- a/force-app/main/default/classes/RD2_RecurringDonation.cls +++ b/force-app/main/default/classes/RD2_RecurringDonation.cls @@ -89,6 +89,10 @@ public inherited sharing class RD2_RecurringDonation { this(rd, rd?.npe03__Donations__r); } + public npe03__Recurring_Donation__c getRecord (){ + return rd; + } + /** * @description Constructor * @param rd Recurring Donation record diff --git a/force-app/main/default/classes/RD2_ScheduleService.cls b/force-app/main/default/classes/RD2_ScheduleService.cls index 1ebf1ae9083..c7d46c19d0e 100644 --- a/force-app/main/default/classes/RD2_ScheduleService.cls +++ b/force-app/main/default/classes/RD2_ScheduleService.cls @@ -1114,10 +1114,7 @@ public without sharing class RD2_ScheduleService { public void savePauseSchedule(Id rdId, RecurringDonationSchedule__c pauseSchedule) { Savepoint sp = Database.setSavepoint(); try { - cancelPauseSchedule(rdId); - insert pauseSchedule; - } catch (Exception e) { Database.rollback(sp); throw new ScheduleException(e.getMessage()); From cea50504e228169355a5d3661701c45d2cc5fd39 Mon Sep 17 00:00:00 2001 From: Daniel Fuller Date: Wed, 17 Aug 2022 16:40:57 -0400 Subject: [PATCH 007/445] display the elevate widget in the BGE form when a gift is made recurring --- .../main/default/lwc/geFormWidget/geFormWidget.html | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/force-app/main/default/lwc/geFormWidget/geFormWidget.html b/force-app/main/default/lwc/geFormWidget/geFormWidget.html index 7e893e1fdbb..9b2c6c9bc6e 100644 --- a/force-app/main/default/lwc/geFormWidget/geFormWidget.html +++ b/force-app/main/default/lwc/geFormWidget/geFormWidget.html @@ -5,13 +5,11 @@ From 7bad60e29f90e1a697feeafaaa58e615ebe21835 Mon Sep 17 00:00:00 2001 From: Reede Stockton Date: Thu, 15 Sep 2022 12:18:43 -0700 Subject: [PATCH 145/445] Working gateway service without error handling --- force-app/main/default/classes/PS_GatewayService.cls | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/force-app/main/default/classes/PS_GatewayService.cls b/force-app/main/default/classes/PS_GatewayService.cls index 57c30647eba..8da817651eb 100644 --- a/force-app/main/default/classes/PS_GatewayService.cls +++ b/force-app/main/default/classes/PS_GatewayService.cls @@ -64,6 +64,10 @@ public with sharing class PS_GatewayService { Map gatewayResponse; gatewayResponse = getGateways(); + // TODO: check for errors here + // return serialized version of response immediately if there are errors + // format message and show a toast in widget + List gateways = new List(); gateways = (List) gatewayResponse.get('gateways'); @@ -90,7 +94,6 @@ public with sharing class PS_GatewayService { public String id; public String alias; public GatewaySettings settings; - public String vendorName; } public class GatewaySettings { @@ -100,7 +103,6 @@ public with sharing class PS_GatewayService { public class GatewayTemplateSetting { public String id; public String alias; - public String vendorName; public String uniqueKey; public Boolean isCreditCardEnabled; public Boolean isACHEnabled; @@ -110,7 +112,6 @@ public with sharing class PS_GatewayService { public GatewayTemplateSetting(ResponseBody rb) { this.id = rb.id; this.alias = rb.alias; - this.vendorName = rb.vendorName; this.uniqueKey = rb.id.left(8) + rb.id.right(12); this.isDefault = false; this.isCreditCardEnabled = false; From 92b67f6e11203f89cbf15bda2cfa1c8a47944adb Mon Sep 17 00:00:00 2001 From: Reede Stockton Date: Thu, 15 Sep 2022 12:21:22 -0700 Subject: [PATCH 146/445] Starter Gateway Service test class --- force-app/main/default/classes/PS_GatewayService_TEST.cls | 7 +++++++ .../default/classes/PS_GatewayService_TEST.cls-meta.xml | 5 +++++ 2 files changed, 12 insertions(+) create mode 100644 force-app/main/default/classes/PS_GatewayService_TEST.cls create mode 100644 force-app/main/default/classes/PS_GatewayService_TEST.cls-meta.xml diff --git a/force-app/main/default/classes/PS_GatewayService_TEST.cls b/force-app/main/default/classes/PS_GatewayService_TEST.cls new file mode 100644 index 00000000000..c4f93f8b2f2 --- /dev/null +++ b/force-app/main/default/classes/PS_GatewayService_TEST.cls @@ -0,0 +1,7 @@ +/** + * Created by reede.stockton on 9/12/22. + */ + +public with sharing class PS_GatewayService_TEST { + +} \ No newline at end of file diff --git a/force-app/main/default/classes/PS_GatewayService_TEST.cls-meta.xml b/force-app/main/default/classes/PS_GatewayService_TEST.cls-meta.xml new file mode 100644 index 00000000000..f928c8e56bc --- /dev/null +++ b/force-app/main/default/classes/PS_GatewayService_TEST.cls-meta.xml @@ -0,0 +1,5 @@ + + + 53.0 + Active + From 3c5c72cfe9066aa32d84050419f091d6095e4423 Mon Sep 17 00:00:00 2001 From: Reede Stockton Date: Fri, 16 Sep 2022 21:23:48 -0700 Subject: [PATCH 147/445] Disable SGE and encrypt gateway Id --- .../default/classes/PS_GatewayManagement.cls | 19 ++ .../default/classes/PS_GatewayService.cls | 7 +- .../geGatewaySelectWidget.html | 194 +++++++++--------- .../geGatewaySelectWidget.js | 47 +++-- .../geGatewaySettings/geGatewaySettings.js | 8 +- .../geTemplateBuilder/geTemplateBuilder.js | 2 +- 6 files changed, 159 insertions(+), 118 deletions(-) diff --git a/force-app/main/default/classes/PS_GatewayManagement.cls b/force-app/main/default/classes/PS_GatewayManagement.cls index d0956a5b5d5..33a807cc194 100644 --- a/force-app/main/default/classes/PS_GatewayManagement.cls +++ b/force-app/main/default/classes/PS_GatewayManagement.cls @@ -79,4 +79,23 @@ public with sharing class PS_GatewayManagement { return gatewayService.getGatewaysByMerchant(); } + @AuraEnabled + public static String getDefaultTemplateId() { + return GE_GiftEntryController.getGiftEntrySettings().Default_Gift_Entry_Template__c; + } + + @AuraEnabled + public static String encryptGatewayId(String gatewayId) { + PS_IntegrationServiceConfig.Service integrationService = new PS_IntegrationServiceConfig.Service(); + return EncodingUtil.base64Encode(Crypto.encryptWithManagedIV('AES128', + Blob.valueOf(integrationService.getMerchantIds().left(16)), Blob.valueOf(gatewayId))); + } + + @AuraEnabled + public static String decryptGatewayId(String encryptedGatewayId) { + PS_IntegrationServiceConfig.Service integrationService = new PS_IntegrationServiceConfig.Service(); + return (Crypto.decryptWithManagedIV('AES128', + Blob.valueOf(integrationService.getMerchantIds().left(16)), + EncodingUtil.base64Decode(encryptedGatewayId))).toString(); + } } \ No newline at end of file diff --git a/force-app/main/default/classes/PS_GatewayService.cls b/force-app/main/default/classes/PS_GatewayService.cls index 8da817651eb..fc591d1d193 100644 --- a/force-app/main/default/classes/PS_GatewayService.cls +++ b/force-app/main/default/classes/PS_GatewayService.cls @@ -93,6 +93,7 @@ public with sharing class PS_GatewayService { public class ResponseBody { public String id; public String alias; + public String vendorName; public GatewaySettings settings; } @@ -102,8 +103,7 @@ public with sharing class PS_GatewayService { public class GatewayTemplateSetting { public String id; - public String alias; - public String uniqueKey; + public String gatewayName; public Boolean isCreditCardEnabled; public Boolean isACHEnabled; public Boolean isDefault; @@ -111,8 +111,7 @@ public with sharing class PS_GatewayService { public GatewayTemplateSetting() {} public GatewayTemplateSetting(ResponseBody rb) { this.id = rb.id; - this.alias = rb.alias; - this.uniqueKey = rb.id.left(8) + rb.id.right(12); + this.gatewayName = rb.alias == null ? rb.vendorName : rb.alias; this.isDefault = false; this.isCreditCardEnabled = false; this.isACHEnabled = false; diff --git a/force-app/main/default/lwc/geGatewaySelectWidget/geGatewaySelectWidget.html b/force-app/main/default/lwc/geGatewaySelectWidget/geGatewaySelectWidget.html index 7f3b62f291a..436197fcef4 100644 --- a/force-app/main/default/lwc/geGatewaySelectWidget/geGatewaySelectWidget.html +++ b/force-app/main/default/lwc/geGatewaySelectWidget/geGatewaySelectWidget.html @@ -1,115 +1,117 @@