Skip to content

Commit

Permalink
Merge pull request #7009 from SalesforceFoundation/feature/240__refun…
Browse files Browse the repository at this point in the history
…d-validation

Refund Validation
  • Loading branch information
lparrott authored Jul 11, 2022
2 parents f2b87bb + 74c8ee5 commit 1b5a60a
Show file tree
Hide file tree
Showing 19 changed files with 820 additions and 161 deletions.
37 changes: 8 additions & 29 deletions force-app/main/default/classes/PMT_Payment_TDTM.cls
Original file line number Diff line number Diff line change
Expand Up @@ -307,39 +307,18 @@ public class PMT_Payment_TDTM extends TDTM_Runnable {
* @return void Does not return anything
********************************************************************************************************/
private void validatePayments(
List<npe01__OppPayment__c> newlist,
List<npe01__OppPayment__c> oldlist,
List<npe01__OppPayment__c> newList,
List<npe01__OppPayment__c> oldList,
TDTM_Runnable.Action triggerAction,
DmlWrapper dmlWrapper) {
if (triggerAction == TDTM_Runnable.Action.AfterInsert || triggerAction == TDTM_Runnable.Action.AfterUpdate) {
for (npe01__OppPayment__c payment : newlist) {
if (isEnforceAccountingDataConsistency) {
enforceAccountingDataConsistency(payment);
}

if (payment.npe01__Paid__c == true && payment.npe01__Written_Off__c == true) {
payment.addError(System.Label.pmtWriteOffPaidPayment);
}
}
}
}

PMT_ValidationService validationService = new PMT_ValidationService(newlist, oldList, triggerAction);
List<ErrorRecord> errorRecords = validationService.validate().getErrors();

/*******************************************************************************************************
* @description If the custom setting to enforce account rules is enabled, it will validate that paid payments
* have a payment date and unpaid payments have a scheduled date.
* @param payment the Payment from trigger new.
* @return void Does not return anything
********************************************************************************************************/
private void enforceAccountingDataConsistency(npe01__OppPayment__c payment) {
if ((payment.npe01__Paid__c == true || payment.npe01__Written_Off__c == true) && payment.npe01__Payment_Date__c == null) {

if (payment.npe01__Paid__c == true) {
payment.addError(System.Label.pmtPaidPaymentDateRequired);
} else {
payment.addError(System.Label.pmtWrittenOffPaymentDateRequired);
for (ErrorRecord error : errorRecords) {
if (error.hasError()) {
error.getRecord().addError(error.getFirstError());
}
} else if (payment.npe01__Paid__c == false && payment.npe01__Written_Off__c == false && payment.npe01__Scheduled_Date__c == null) {
payment.addError(System.Label.pmtUnpaidPaymentScheduledDateRequired);
}
}
}
68 changes: 38 additions & 30 deletions force-app/main/default/classes/PMT_RefundController.cls
Original file line number Diff line number Diff line change
Expand Up @@ -34,17 +34,6 @@
* @description Apex Controller for paymnet refund
*/
public with sharing class PMT_RefundController {
@TestVisible
private static PaymentSelector paymentSelector {
get {
if (paymentSelector == null) {
paymentSelector = new PaymentSelector();
}
return paymentSelector;
}
set;
}

@TestVisible
private static PMT_RefundService refundService {
get {
Expand All @@ -65,11 +54,15 @@ public with sharing class PMT_RefundController {
return refundView;
}

npe01__OppPayment__c payment = paymentSelector.getPaymentRecordForRefund(paymentId);
refundView.paymentDate = payment.npe01__Payment_Date__c;
refundView.paymentAmount = payment.npe01__Payment_Amount__c;
refundService.withOriginalPaymentIds(new Set<Id>{paymentId});
PMT_RefundService.RefundInfo refundInfo = refundService.getRefundInfoFor(paymentId);

refundView.paymentDate = refundInfo.originalPayment.npe01__Payment_Date__c;
refundView.paymentAmount = refundInfo.originalPayment.npe01__Payment_Amount__c;

if (UserInfo.isMultiCurrencyOrganization()) {
refundView.currencyCode = (String) payment.get(UTIL_Currency.CURRENCY_ISO_CODE_FIELD);
refundView.currencyCode = (String) refundInfo.originalPayment.get(UTIL_Currency.CURRENCY_ISO_CODE_FIELD);

} else {
refundView.currencyCode = null;
}
Expand All @@ -85,27 +78,29 @@ public with sharing class PMT_RefundController {
return refundView;
}

npe01__OppPayment__c originalPayment = paymentSelector.getPaymentRecordForRefund(paymentId);
ensurePaymentIsRefundable(originalPayment, refundView);
refundService.withOriginalPaymentIds(new Set<Id>{paymentId});
PMT_RefundService.RefundInfo refundInfo = refundService.getRefundInfoFor(paymentId);

if (!isPaymentRefundable(refundInfo.originalPayment)) {
handleError(refundView, System.Label.pmtPaymentNotRefundable);
return refundView;
}

if (refundView.isSuccess != null && !refundView.isSuccess) {
return refundView;
}

if (isElevatePayment(originalPayment.Elevate_Payment_ID__c)) {
processElevateRefund(originalPayment, refundView);
if (isElevatePayment(refundInfo.originalPayment.Elevate_Payment_ID__c)) {
processElevateRefund(refundInfo.originalPayment, refundView);
} else {
processNonElevateRefund(originalPayment, refundView);
processNonElevateRefund(refundInfo.originalPayment, refundView);
}

return refundView;
}

private static void ensurePaymentIsRefundable(npe01__OppPayment__c originalPayment, RefundView refundView) {
if (originalPayment.DebitType__c == PMT_RefundService.FULL_REFUND || originalPayment.npe01__Payment_Amount__c <= 0) {
refundView.isSuccess = false;
refundView.errorMessage = System.Label.pmtPaymentNotRefundable;
}
private static Boolean isPaymentRefundable(npe01__OppPayment__c originalPayment) {
return String.isBlank(originalPayment.DebitType__c) && originalPayment.npe01__Payment_Amount__c > 0;
}


Expand All @@ -127,18 +122,33 @@ public with sharing class PMT_RefundController {
private static void processNonElevateRefund(npe01__OppPayment__c originalPayment, RefundView refundView) {
refundService.withOriginalPayments(new List<npe01__OppPayment__c>{originalPayment});
refundService.processNonElevateRefunds();

npe01__OppPayment__c refundRecord = refundService.getRefundRecords()[0];

List<ErrorRecord> errorRecords = new PMT_ValidationService()
.validateRefund(refundRecord, refundService.getRefundInfoFor(refundRecord.OriginalPayment__c))
.getErrors();
if (errorRecords != null && !errorRecords.isEmpty()) {
handleError(refundView, errorRecords[0].getFirstError());
return;
}

refundService.processDML();

ERR_Handler.Errors errors = refundService.getErrors();
if (!errors.errorRecords.isEmpty()) {
refundView.isSuccess = false;
refundView.errorMessage = errors.errorRecords[0].Full_Message__c;
handleError(refundView, errors.errorRecords[0].Full_Message__c);
} else {
refundView.redirectToPaymentId = refundService.getRefundRecords()[0].Id;
refundView.isSuccess = true;
}
}

private static void handleError(RefundView refundView, String errorMessage) {
refundView.isSuccess = false;
refundView.errorMessage = errorMessage;
}

private static Boolean hasRequiredPermission() {
return UTIL_Permissions.canCreate('npe01__oppPayment__c', false) &&
UTIL_Permissions.canUpdate('npe01__oppPayment__c', false) &&
Expand All @@ -148,15 +158,13 @@ public with sharing class PMT_RefundController {
UTIL_Permissions.canCreate('npe01__oppPayment__c', String.valueOf(npe01__oppPayment__c.OriginalPayment__c), false);
}

private static ReFundView processResponse(RefundView refundView, UTIL_Http.Response response) {
private static void processResponse(RefundView refundView, UTIL_Http.Response response) {
refundView.isSuccess = response.statusCode == UTIL_Http.STATUS_CODE_CREATED
|| response.statusCode == UTIL_Http.STATUS_CODE_OK;
if (!refundView.isSuccess) {
refundView.errorMessage = response.getErrorMessages();

}

return refundView;
}

public class RefundView {
Expand Down
107 changes: 53 additions & 54 deletions force-app/main/default/classes/PMT_RefundController_TEST.cls
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@
*/
@IsTest
public with sharing class PMT_RefundController_TEST {
@isTest
@IsTest
private static void userWithNoPermissionShouldNotBeAbleToPerformAnyRefundAction() {
User readOnlyUser = UTIL_UnitTestData_TEST.createUser(UTIL_UnitTestData_TEST.PROFILE_READONLY_USER);

Expand All @@ -48,17 +48,18 @@ public with sharing class PMT_RefundController_TEST {
}
}

@isTest
@IsTest
private static void verifyRefundCalloutIsMadeWhenPaymentIsAnElevateRecord() {
PS_IntegrationServiceConfig_TEST.enableElevateIntegrationService();
PaymentSelector_TEST.Stub selectorStub = new PaymentSelector_TEST.Stub();

npe01__OppPayment__c payment = getElevatePaymentRecord();
selectorStub.paymentRecords = new List<npe01__OppPayment__c>{ payment };
PMT_RefundService refundService = new PMT_RefundService()
.withOriginalPayments(new List<npe01__OppPayment__c>{payment});

PMT_RefundController.paymentSelector = (PaymentSelector) Test.createStub(
PaymentSelector.class,
selectorStub
refundService.originalPaymentWithRefunds.put(
payment.Id, new PMT_RefundService.RefundInfo(payment)
);
PMT_RefundController.RefundService = refundService;

UTIL_Http_TEST.CalloutMock calloutMock = UTIL_Http_TEST.mockSuccessCalloutResponse(null);

Expand All @@ -74,19 +75,19 @@ public with sharing class PMT_RefundController_TEST {
'The redirect Id should set to the original payment Id when the payment is an Elevate record');
}

@isTest
@IsTest
private static void verifyTheErrorMessageWillBePopulatedCorrectlyWhenElevateCalloutFail() {
PS_IntegrationServiceConfig_TEST.enableElevateIntegrationService();
UTIL_Http_TEST.CalloutMock calloutMock = UTIL_Http_TEST.mockNotFoundCalloutResponse();

PaymentSelector_TEST.Stub selectorStub = new PaymentSelector_TEST.Stub();
npe01__OppPayment__c payment = getElevatePaymentRecord();
selectorStub.paymentRecords = new List<npe01__OppPayment__c>{ payment };
PMT_RefundService refundService = new PMT_RefundService()
.withOriginalPayments(new List<npe01__OppPayment__c>{payment});

PMT_RefundController.paymentSelector = (PaymentSelector) Test.createStub(
PaymentSelector.class,
selectorStub
refundService.originalPaymentWithRefunds.put(
payment.Id, new PMT_RefundService.RefundInfo(payment)
);
PMT_RefundController.RefundService = refundService;

Test.startTest();
PMT_RefundController.RefundView refundView = PMT_RefundController.processRefund(payment.Id);
Expand All @@ -96,69 +97,48 @@ public with sharing class PMT_RefundController_TEST {
'1 callout should be made when the refund callout is made');
System.assertEquals(false, refundView.isSuccess,
'The Refund View should mark as not success when the callout failed');
System.assertEquals(System.Label.pmtPaymentNotFund, refundView.errorMessage,
System.assertEquals(System.Label.pmtPaymentNotFound, refundView.errorMessage,
'The error message should be added to the view when the callout failed');
}

@isTest
@IsTest
private static void verifyRefundRecordIdShouldBeReturnedOnSuccessfulRefund() {
PS_IntegrationServiceConfig_TEST.enableElevateIntegrationService();
npe01__OppPayment__c refundRecord = getPaymentRecord();
npe01__OppPayment__c payment = getPaymentRecord();
npe01__OppPayment__c refund = getFullRefundRecord(payment.Id);

PMT_RefundService_TEST.Stub refundServiceStub = new PMT_RefundService_TEST.Stub();
refundServiceStub.refundRecords = new List<npe01__OppPayment__c>{ refundRecord };

refundServiceStub.refundRecords = new List<npe01__OppPayment__c>{ refund };
refundServiceStub.refundInfo = new PMT_RefundService.RefundInfo( payment );
PMT_RefundController.refundService = (PMT_RefundService) Test.createStub(
PMT_RefundService.class,
refundServiceStub
);

PaymentSelector_TEST.Stub selectorStub = new PaymentSelector_TEST.Stub();
selectorStub.paymentRecords = new List<npe01__OppPayment__c>{ refundRecord };

PMT_RefundController.paymentSelector = (PaymentSelector) Test.createStub(
PaymentSelector.class,
selectorStub
);

Test.startTest();
PMT_RefundController.RefundView refundView = PMT_RefundController.processRefund(UTIL_UnitTestData_TEST.mockId(npe01__OppPayment__c.SObjectType));
PMT_RefundController.RefundView refundView = PMT_RefundController.processRefund(payment.Id);
Test.stopTest();

System.assertEquals(refundRecord.Id, refundView.redirectToPaymentId,
System.assertEquals(refund.Id, refundView.redirectToPaymentId,
'The refund record should exist when the refund is successful');
}

@isTest
@IsTest
private static void verifyDoubleRefundErrorShouldBeReturnWhenRefundProcessFail() {
PS_IntegrationServiceConfig_TEST.enableElevateIntegrationService();

ERR_Handler.Errors errorResult = new ERR_Handler.Errors();
errorResult.errorsExist = true;
errorResult.errorRecords.add(
ERR_Handler.createError(System.Label.pmtPaymentNotRefundable, 'random')
);

npe01__OppPayment__c payment = getPaymentRecord();

payment = addRefundRelatedList(payment, new List<npe01__OppPayment__c>{getFullRefundRecord(payment.Id)});
npe01__OppPayment__c doubleRefund = getFullRefundRecord(payment.Id);
doubleRefund.Id = null;
PMT_RefundService_TEST.Stub refundServiceStub = new PMT_RefundService_TEST.Stub();
refundServiceStub.errors = errorResult;

refundServiceStub.refundRecords = new List<npe01__OppPayment__c>{ doubleRefund };
refundServiceStub.refundInfo = new PMT_RefundService.RefundInfo( payment );
PMT_RefundController.refundService = (PMT_RefundService) Test.createStub(
PMT_RefundService.class,
refundServiceStub
);

PaymentSelector_TEST.Stub selectorStub = new PaymentSelector_TEST.Stub();
npe01__OppPayment__c refund = getRefundRecord();

selectorStub.paymentRecords = new List<npe01__OppPayment__c>{ refund };

PMT_RefundController.paymentSelector = (PaymentSelector) Test.createStub(
PaymentSelector.class,
selectorStub
);

Test.startTest();
PMT_RefundController.RefundView refundView = PMT_RefundController.processRefund(UTIL_UnitTestData_TEST.mockId(npe01__OppPayment__c.SObjectType));
PMT_RefundController.RefundView refundView = PMT_RefundController.processRefund(payment.Id);
Test.stopTest();

System.assertEquals(false, refundView.isSuccess,
Expand All @@ -176,20 +156,39 @@ public with sharing class PMT_RefundController_TEST {
}

private static npe01__OppPayment__c getPaymentRecord() {

Opportunity opportunity = new Opportunity(
Id = UTIL_UnitTestData_TEST.mockId(Opportunity.SObjectType),
Amount = 1000
);

return new npe01__OppPayment__c(
Id = UTIL_UnitTestData_TEST.mockId(npe01__OppPayment__c.SObjectType),
npe01__Paid__c = true,
npe01__Payment_Amount__c = 250,
npe01__Opportunity__c = UTIL_UnitTestData_TEST.mockId(Opportunity.SObjectType)
npe01__Opportunity__c = opportunity.Id
);
}

private static npe01__OppPayment__c getRefundRecord() {
private static npe01__OppPayment__c getFullRefundRecord(Id originalPaymentId) {
return new npe01__OppPayment__c(
Id = UTIL_UnitTestData_TEST.mockId(npe01__OppPayment__c.SObjectType),
npe01__Payment_Amount__c = -250,
DebitType__c = PMT_RefundService.FULL_REFUND,
npe01__Opportunity__c = UTIL_UnitTestData_TEST.mockId(Opportunity.SObjectType)
npe01__Opportunity__c = UTIL_UnitTestData_TEST.mockId(Opportunity.SObjectType),
OriginalPayment__c = originalPaymentId
);
}

private static npe01__OppPayment__c addRefundRelatedList(npe01__OppPayment__c payment, List<SObject> records) {
String paymentRecord = JSON.serialize(payment);
paymentRecord = paymentRecord.left(paymentRecord.length()-1);

String objJson = JSON.serialize(records);
paymentRecord += ',"' + UTIL_Namespace.StrTokenNSPrefix('Refunds__r') + '":{"totalSize":' + records.size() + ',"done":true,"records":' + objJson + '}';

paymentRecord += '}';

return (npe01__OppPayment__c)JSON.deserialize(paymentRecord, npe01__OppPayment__c.class);
}
}
Loading

0 comments on commit 1b5a60a

Please sign in to comment.