Skip to content

Commit

Permalink
Merge pull request #7080 from SalesforceFoundation/feature/242
Browse files Browse the repository at this point in the history
Feature 242
  • Loading branch information
lparrott committed Nov 22, 2022
2 parents db13109 + fdf146d commit 7ed61ee
Show file tree
Hide file tree
Showing 178 changed files with 7,438 additions and 2,278 deletions.
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ Referenced Packages
*.sublime-project
*.sublime-workspace
**/.sfdx/
**/.sf/
**/.vscode/
**/.idea/
**/.mypy_cache/
Expand All @@ -42,7 +43,8 @@ robot/Cumulus/results/
datasets/dev_org/test_data.db
*.db
.cci

.sfdx
.sf
# LWC
force-app/main/default/lwc/.eslintrc.json
/node_modules
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

* <a href="https://trailhead.salesforce.com/trailblazer-community/groups/0F94S000000kHitSAE?tab=discussion&sort=LAST_MODIFIED_DATE_DESC" target="_blank">Ask questions or get help</a>
* <a href="https://github.com/SalesforceFoundation/NPSP/issues/new" target="_blank">Log a confirmed Issue</a> or <a href="https://ideas.salesforce.com/s/search#t=All&sort=relevancy" target="_blank">Feature Request</a>
* <a href="https://powerofus.force.com/NPSP_Documentation" target="_blank">User Documentation</a>
* <a href="https://help.salesforce.com/s/articleView?id=sfdo.Nonprofit_Success_Pack.htm&type=5" target="_blank">Nonprofit Success Pack (NPSP) Documentation</a>
* Check out existing <a href="https://github.com/SalesforceFoundation/NPSP/labels/bug" target="_blank">bugs</a> and <a href="https://ideas.salesforce.com/s/search#t=All&sort=relevancy&f:@sfcategoryfull=[Nonprofit%7CNonprofit%20Success%20Pack]" target="_blank">feature and enhancement requests.</a>
* <a href="https://github.com/SalesforceFoundation/NPSP/releases" target="_blank">Release Notes and Beta Releases</a>

Expand Down
63 changes: 58 additions & 5 deletions force-app/main/default/classes/ALLO_Allocations_TDTM.cls
Original file line number Diff line number Diff line change
Expand Up @@ -226,14 +226,22 @@ public class ALLO_Allocations_TDTM extends TDTM_Runnable {
private void runPaymentTriggerHandler(List<SObject> newlist, List<SObject> oldlist,
TDTM_Runnable.Action triggerAction, Schema.DescribeSObjectResult objResult) {


//do not run if Payment allocations are disabled
if (isPaymentAllocationsEnabled()) {
processPaymentAllocations(newlist, oldlist, triggerAction);
}

if (triggerAction == TDTM_Runnable.Action.AfterInsert || triggerAction == TDTM_Runnable.Action.AfterUpdate) {
handleAllocationsAndOpportunities(newList, oldList);
}
}

private void processPaymentAllocations(List<SObject> newlist, List<SObject> oldlist, TDTM_Runnable.Action triggerAction) {
List<npe01__OppPayment__c> listPmtsForProcessing = new List<npe01__OppPayment__c>();
List<npe01__OppPayment__c> pmtsWithNullOldAmount = new List<npe01__OppPayment__c>();
List<npe01__OppPayment__c> pmtsNeedingAllocations = new List<npe01__OppPayment__c>();

//do not run if Payment allocations are disabled
if (!isPaymentAllocationsEnabled()) {
return;
}

for (integer i=0; i<newList.size(); i++) {
npe01__OppPayment__c pmt = (npe01__OppPayment__c) newlist[i];
Expand Down Expand Up @@ -284,6 +292,41 @@ public class ALLO_Allocations_TDTM extends TDTM_Runnable {
}
}

private void handleAllocationsAndOpportunities(
List<npe01__OppPayment__c> newList,
List<npe01__OppPayment__c> oldList
) {
List<npe01__OppPayment__c> refunds = new List<npe01__OppPayment__c>();
List<npe01__OppPayment__c> oldRefunds = new List<npe01__OppPayment__c>();
for (Integer i = 0; i < newList.size(); i++) {
if (newList[i].npe01__Payment_Amount__c < 0 && String.isBlank(newList[i].Elevate_Payment_ID__c)) {
refunds.add(newList[i]);
if ( oldList != null) {
oldRefunds.add(oldList[i]);
}
}
}

if (refunds.isEmpty()) {
return;
}

PMT_RefundService refundService = new PMT_RefundService()
.withRefundRecords(refunds)
.withOldRefundRecords(oldRefunds);

refundService.adjustAllocationsAndOpportunities()
.updateAllocationsAndOpportunities();

List<ErrorRecord> errorRecords = refundService.getErrors();

for (ErrorRecord error : errorRecords) {
if (error.hasError()) {
error.getRecord().addError(error.getFirstError());
}
}
}

/*******************************************************************************************************
* @description Allocations before trigger handler on GAU Allocation. Validates allocation data per
* object and per parent object to avoid badly created allocations, exceeding opportunity amount,
Expand Down Expand Up @@ -747,8 +790,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);
Expand Down
59 changes: 57 additions & 2 deletions force-app/main/default/classes/ALLO_Allocations_TEST.cls
Original file line number Diff line number Diff line change
Expand Up @@ -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)
);
Expand All @@ -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 no error is generated if Opportunity Amount becomes 0 and a non zero fixed
* Amount Allocation exists
********************************************************************************************************/
@isTest
private static void testFixupForNonZeroFixedAmountAllocationIfOpportunityAmountIsZero() {
List<General_Accounting_Unit__c> gaus = new List<General_Accounting_Unit__c>();

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<Account> accs = UTIL_UnitTestData_TEST.createMultipleTestAccounts(1, null);

insert accs;

List<Opportunity> opps = UTIL_UnitTestData_TEST.oppsForAccountList(accs, null, UTIL_UnitTestData_TEST.getClosedWonStage(), System.today(), 10, null, null);

insert opps;
Opportunity opportunity = opps[0];

Test.startTest();

List<Allocation__c> newAllos = new List<Allocation__c>();

// Fix Amount Allocation for $10 to GAU 1
newAllos.add(
createOppAllocation(gau1.Id, opportunity.Id, 10, null)
);

insert newAllos;

try {
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());
}

opportunity.Amount = 0;
update opportunity;
Map<Id, Allocation__c> oppAllos = getOpportunityAllocationsByGAU(opportunity.Id);
for(Id gauId : oppAllos.keySet()) {
System.assertEquals(0, oppAllos.get(gauId).Amount__c, 'Allocaiton Amount should be 0');
}

Test.stopTest();
Expand Down
56 changes: 56 additions & 0 deletions force-app/main/default/classes/AllocationSelector.cls
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
/*
Copyright (c) 2022, Salesforce.org
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of Salesforce.org nor the names of
its contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
/**
* @author Salesforce.org
* @date 2022
* @group Allocation
* @description Selector Class for Allocation__c Sobject
*/
public with sharing class AllocationSelector {
public List<Allocation__c> getOpportunityAllocations(Set<Id> oppIds) {
String soql = new UTIL_Query()
.withFrom(Allocation__c.SObjectType)
.withSelectFields(getStandardAllocationFields())
.withWhere('Opportunity__c IN: oppIds')
.build();

return Database.query(soql);
}

private Set<String> getStandardAllocationFields() {
return new Set<String> {
String.valueOf(Allocation__c.Percent__c),
String.valueOf(Allocation__c.Amount__c),
String.valueOf(Allocation__c.Opportunity__c),
UTIL_Namespace.StrTokenNSPrefix('Allocation__c.Opportunity__r.Amount'),
String.valueOf(Allocation__c.General_Accounting_Unit__c)
};
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<ApexClass xmlns="http://soap.sforce.com/2006/04/metadata">
<apiVersion>55.0</apiVersion>
<status>Active</status>
</ApexClass>
60 changes: 60 additions & 0 deletions force-app/main/default/classes/AllocationSelector_TEST.cls
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
/*
Copyright (c) 2022, Salesforce.org
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of Salesforce.org nor the names of
its contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
/**
* @author Salesforce.org
* @date 2022
* @group Opportunity
* @description Test and Mock class for OpportunitySelector
*/
@isTest
public with sharing class AllocationSelector_TEST {

public class Stub implements System.StubProvider {
public List<Allocation__c> allocationRecords;

public Object handleMethodCall(
Object stubbedObject,
String methodName,
Type returnType,
List<Type> paramTypes,
List<String> paramNames,
List<Object> args
) {
switch on methodName {
when 'getOpportunityAllocations' {
return allocationRecords;

} when else {
return null;
}
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<ApexClass xmlns="http://soap.sforce.com/2006/04/metadata">
<apiVersion>55.0</apiVersion>
<status>Active</status>
</ApexClass>
12 changes: 7 additions & 5 deletions force-app/main/default/classes/CRLP_Batch_Base_Skew.cls
Original file line number Diff line number Diff line change
Expand Up @@ -232,7 +232,7 @@ public abstract class CRLP_Batch_Base_Skew extends CRLP_Batch_Base {
* @param parentObjectField
* @param detailRecords
* @param parentKeyField
* @return Boolean true if lastParentId has no associated detail records in a GAU rollup
* @return Boolean false if lastParentId has no associated detail records in a GAU rollup
*/
@TestVisible
private Boolean hasLastParentDetails(
Expand All @@ -241,14 +241,16 @@ public abstract class CRLP_Batch_Base_Skew extends CRLP_Batch_Base {
List<SObject> detailRecords,
String parentKeyField
) {
if (this.jobType == CRLP_RollupProcessingOptions.RollupType.GAU &&
Integer drSize = detailRecords.size();
if (drSize == 0 || (this.jobType == CRLP_RollupProcessingOptions.RollupType.GAU &&
lastParentId != CRLP_Rollup_SVC.getParentIdFromRecord(
detailRecords[detailRecords.size() - 1], parentKeyField, parentObjectField
detailRecords[drSize - 1], parentKeyField, parentObjectField
)
) {
)) {
return false;
} else {
return true;
}
return true;
}

/********************************************************************************************************
Expand Down
17 changes: 17 additions & 0 deletions force-app/main/default/classes/CRLP_GAU_BATCH_TEST.cls
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,23 @@ private with sharing class CRLP_GAU_BATCH_TEST {
System.assertEquals(false,
gauBatch.hasLastParentDetails(lastParentId, null, detailRecords, parentKeyField));
}

@IsTest
private static void shouldDetermineLastParentIdHasNoDetailsEmptyDetailRecords() {
String parentKeyField = 'General_Accounting_Unit__c';

General_Accounting_Unit__c gau = new General_Accounting_Unit__c(
Id = UTIL_UnitTestData_TEST.mockId(General_Accounting_Unit__c.SObjectType));
General_Accounting_Unit__c gau2 = new General_Accounting_Unit__c(
Id = UTIL_UnitTestData_TEST.mockId(General_Accounting_Unit__c.SObjectType));

List<Allocation__c> detailRecords = new List<Allocation__c>();

CRLP_GAU_BATCH gauBatch = new CRLP_GAU_BATCH(CRLP_RollupProcessingOptions.RollupTypeFilter.All);
Id lastParentId = gau2.Id;
System.assertEquals(false,
gauBatch.hasLastParentDetails(lastParentId, null, detailRecords, parentKeyField));
}

@IsTest
private static void shouldDetermineLastParentIdHasDetails() {
Expand Down
1 change: 1 addition & 0 deletions force-app/main/default/classes/CallableApiParameters.cls
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ public class CallableApiParameters {
public static final String PARAM_START_DATE = 'StartDate';
public static final String PARAM_END_DATE = 'EndDate';
public static final String PARAM_PAUSE_DATA = 'PauseData';
public static final String PARAM_REFUND_RECORDS = 'RefundRecords';

private Map<String, Object> params;

Expand Down
Loading

0 comments on commit 7ed61ee

Please sign in to comment.