Skip to content

Commit

Permalink
v1.6.26 - Rollup Grouping support, FIRST/LAST/MOST/AVERAGE bugfixes (#…
Browse files Browse the repository at this point in the history
…592)

* Tidies up #587
* Creates RollupGrouping__mdt custom metadata type to allow multiple rollup operations to roll up to the same field
* Fixes #589 with proper stateful tracking for batches, with the potential to roll this out to SUM/COUNT operations in the future as well
  • Loading branch information
jamessimone committed May 17, 2024
1 parent aebf83b commit 8fa1867
Show file tree
Hide file tree
Showing 27 changed files with 707 additions and 395 deletions.
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,12 @@ As well, don't miss [the Wiki](../../wiki), which includes even more info for co

## Deployment & Setup

<a href="https://login.salesforce.com/packaging/installPackage.apexp?p0=04t6g000008Ob22AAC">
<a href="https://login.salesforce.com/packaging/installPackage.apexp?p0=04t6g000008ObCnAAK">
<img alt="Deploy to Salesforce"
src="./media/deploy-package-to-prod.png">
</a>

<a href="https://test.salesforce.com/packaging/installPackage.apexp?p0=04t6g000008Ob22AAC">
<a href="https://test.salesforce.com/packaging/installPackage.apexp?p0=04t6g000008ObCnAAK">
<img alt="Deploy to Salesforce Sandbox"
src="./media/deploy-package-to-sandbox.png">
</a>
Expand Down Expand Up @@ -133,6 +133,7 @@ Within the `Rollup__mdt` custom metadata type, add a new record with fields:
- Both CONCAT and CONCAT_DISTINCT separate values with commas by default in the rollup field itself, but you can use `Concat Delimiter` to change that
- CONCAT / CONCAT_DISTINCT / FIRST / LAST / MOST operations support the usage of `Rollup Order By` Custom Metadata to stipulate how to sort records, or how to sort the concatenated values. For MOST, using `Rollup Order By` CMDT allows you to dictate how a tiebreaker for rollup values with the most instances can be handled (should a tie occur occur).
- `Rollup Control` - link to the Org Defaults for controlling rollups, or set a specific Rollup Control CMDT to be used with this rollup. Multiple rollups can be tied to one specific Control record, or simply use the Org Default record (included) for all of your rollups.
- `Rollup Grouping` (optional) - lookup field to `RollupGrouping__mdt`, a parent-level CMDT record used when rolling values from two different children objects up to the same parent field
- `Currency Field Mapping (Comma-separated)` (optional) - for organizations using Advanced Currency Management with Dated Exchange Rates, fill out this field if you are rolling up a currency field and you'd like to customize which field (or parent-level field) is used as the Date or Datetime to be matched with its corresponding Dated Exchange Rate. As an example, Opportunity Splits use `Opportunity,CloseDate` here by default - if you are using a parent-level field, the relationship name for it is the first value (thus, `Opportunity,CloseDate` is correct - `OpportunityId,CloseDate` would be incorrect).
- `Concat Delimiter` (optional) - for `CONCAT` and `CONCAT_DISTINCT` operations, the delimiter used between text defaults to a comma (unless you are rolling up to a multi-select picklist, in which case it defaults to a semi-colon), but you can override the default delimiter here. At this time, only single character delimiters are supported - please file [an issue](/issues) if you are looking to use multi-character delimiters!
- `Changed Fields On Child Object` (optional) - comma-separated list of field API Names to filter child records - only records where at least one of the fields listed has changed on an update will end up triggering a rollup. If you are using a `Child Object Where Clause`, I would not recommend using this field
Expand Down
105 changes: 44 additions & 61 deletions extra-tests/classes/RollupCalcItemSorterTests.cls
Original file line number Diff line number Diff line change
Expand Up @@ -12,18 +12,18 @@ private class RollupCalcItemSorterTests {
Date severalDaysAgo = System.today().addDays(-2);
Opportunity expectedFirstItem = new Opportunity(Amount = null, CloseDate = severalDaysAgo);
Opportunity expectedSecondItem = new Opportunity(Amount = 1, CloseDate = severalDaysAgo);
List<RollupCalculator.WinnowResult> oppsToSort = new List<RollupCalculator.WinnowResult>{
new RollupCalculator.WinnowResult(new Opportunity(Amount = 1, CloseDate = System.today())),
List<Opportunity> oppsToSort = new List<Opportunity>{
new Opportunity(Amount = 1, CloseDate = System.today()),
// this record should essentially be thrown out of sorting since it "loses" on the first ordering,
// which is on Amount
new RollupCalculator.WinnowResult(new Opportunity(Amount = 3, CloseDate = severalDaysAgo.addDays(-1))),
new RollupCalculator.WinnowResult(expectedSecondItem),
new RollupCalculator.WinnowResult(expectedFirstItem)
new Opportunity(Amount = 3, CloseDate = severalDaysAgo.addDays(-1)),
expectedSecondItem,
expectedFirstItem
};
oppsToSort.sort(sorter);

System.assertEquals(expectedFirstItem, oppsToSort[0].item);
System.assertEquals(expectedSecondItem, oppsToSort[1].item);
System.assertEquals(expectedFirstItem, oppsToSort[0]);
System.assertEquals(expectedSecondItem, oppsToSort[1]);
}

@IsTest
Expand All @@ -41,18 +41,13 @@ private class RollupCalcItemSorterTests {
Opportunity expectedThirdItem = new Opportunity(Amount = 2, CloseDate = today, Name = 'a');
Opportunity expectedSecondItem = new Opportunity(Amount = 1, CloseDate = today, Name = 'c');
Opportunity expectedFourthItem = new Opportunity(Amount = 2, CloseDate = today.addDays(1), Name = 'a');
List<RollupCalculator.WinnowResult> oppsToSort = new List<RollupCalculator.WinnowResult>{
new RollupCalculator.WinnowResult(expectedSecondItem),
new RollupCalculator.WinnowResult(expectedFourthItem),
new RollupCalculator.WinnowResult(expectedThirdItem),
new RollupCalculator.WinnowResult(expectedFirstItem)
};
List<Opportunity> oppsToSort = new List<Opportunity>{ expectedSecondItem, expectedFourthItem, expectedThirdItem, expectedFirstItem };
oppsToSort.sort(sorter);

System.assertEquals(expectedFirstItem, oppsToSort[0].item);
System.assertEquals(expectedSecondItem, oppsToSort[1].item);
System.assertEquals(expectedThirdItem, oppsToSort[2].item);
System.assertEquals(expectedFourthItem, oppsToSort[3].item);
System.assertEquals(expectedFirstItem, oppsToSort[0]);
System.assertEquals(expectedSecondItem, oppsToSort[1]);
System.assertEquals(expectedThirdItem, oppsToSort[2]);
System.assertEquals(expectedFourthItem, oppsToSort[3]);
}

@IsTest
Expand All @@ -67,15 +62,15 @@ private class RollupCalcItemSorterTests {
Date severalDaysAgo = System.today().addDays(-2);
Opportunity expectedFirstItem = new Opportunity(Amount = 1, CloseDate = System.today());
Opportunity expectedSecondItem = new Opportunity(Amount = 3, CloseDate = severalDaysAgo.addDays(-1));
List<RollupCalculator.WinnowResult> oppsToSort = new List<RollupCalculator.WinnowResult>{
new RollupCalculator.WinnowResult(new Opportunity(Amount = 3, CloseDate = severalDaysAgo.addDays(-1))),
new RollupCalculator.WinnowResult(expectedSecondItem),
new RollupCalculator.WinnowResult(expectedFirstItem)
List<Opportunity> oppsToSort = new List<Opportunity>{
new Opportunity(Amount = 3, CloseDate = severalDaysAgo.addDays(-1)),
expectedSecondItem,
expectedFirstItem
};
oppsToSort.sort(sorter);

System.assertEquals(expectedFirstItem, oppsToSort[0].item);
System.assertEquals(expectedSecondItem, oppsToSort[1].item);
System.assertEquals(expectedFirstItem, oppsToSort[0]);
System.assertEquals(expectedSecondItem, oppsToSort[1]);
}

@IsTest
Expand All @@ -93,33 +88,29 @@ private class RollupCalcItemSorterTests {
Opportunity expectedSecondItem = new Opportunity(Amount = 5, CloseDate = today);
Opportunity expectedThirdItem = new Opportunity(Amount = 1, CloseDate = today);

List<RollupCalculator.WinnowResult> oppsToSort = new List<RollupCalculator.WinnowResult>{
new RollupCalculator.WinnowResult(expectedThirdItem),
new RollupCalculator.WinnowResult(expectedFirstItem),
new RollupCalculator.WinnowResult(expectedSecondItem)
};
List<Opportunity> oppsToSort = new List<Opportunity>{ expectedThirdItem, expectedFirstItem, expectedSecondItem };
oppsToSort.sort(sorter);

System.assertEquals(expectedFirstItem, oppsToSort[0].item);
System.assertEquals(expectedSecondItem, oppsToSort[1].item);
System.assertEquals(expectedThirdItem, oppsToSort[2].item);
System.assertEquals(expectedFirstItem, oppsToSort[0]);
System.assertEquals(expectedSecondItem, oppsToSort[1]);
System.assertEquals(expectedThirdItem, oppsToSort[2]);
}

@IsTest
static void shouldProperlySortPicklists() {
RollupCalcItemSorter sorter = new RollupCalcItemSorter(new List<RollupOrderBy__mdt>{ new RollupOrderBy__mdt(Ranking__c = 0, FieldName__c = 'Industry') });

List<Schema.PicklistEntry> picklistEntries = Account.Industry.getDescribe().getPicklistValues();
List<RollupCalculator.WinnowResult> accs = new List<RollupCalculator.WinnowResult>();
List<Account> accs = new List<Account>();

for (Integer reverseIndex = picklistEntries.size() - 1; reverseIndex >= 0; reverseIndex--) {
Schema.PicklistEntry entry = picklistEntries[reverseIndex];
accs.add(new RollupCalculator.WinnowResult(new Account(Name = entry.getValue(), Industry = entry.getValue())));
accs.add(new Account(Name = entry.getValue(), Industry = entry.getValue()));
}
accs.sort(sorter);

for (Integer index = 0; index < accs.size(); index++) {
System.assertEquals(picklistEntries[index].getValue(), accs[index].item.get(Account.Industry), 'Account at index: ' + index + ' should have matched');
System.assertEquals(picklistEntries[index].getValue(), accs[index].Industry, 'Account at index: ' + index + ' should have matched');
}
}

Expand All @@ -138,14 +129,11 @@ private class RollupCalcItemSorterTests {
}
);

List<RollupCalculator.WinnowResult> opps = new List<RollupCalculator.WinnowResult>{
new RollupCalculator.WinnowResult(second),
new RollupCalculator.WinnowResult(opp)
};
List<Opportunity> opps = new List<Opportunity>{ second, opp };
opps.sort(sorter);

System.assertEquals(opp, opps[0].item);
System.assertEquals(second, opps[1].item);
System.assertEquals(opp, opps[0]);
System.assertEquals(second, opps[1]);
}

@IsTest
Expand All @@ -156,37 +144,32 @@ private class RollupCalcItemSorterTests {

Opportunity expectedFirstItem = new Opportunity(Amount = null);
Opportunity expectedSecondItem = new Opportunity(Amount = 1);
List<RollupCalculator.WinnowResult> oppsToSort = new List<RollupCalculator.WinnowResult>{
new RollupCalculator.WinnowResult(new Opportunity(Amount = 1)),
new RollupCalculator.WinnowResult(new Opportunity(Amount = 3)),
new RollupCalculator.WinnowResult(expectedSecondItem),
new RollupCalculator.WinnowResult(expectedFirstItem)
};
List<Opportunity> oppsToSort = new List<Opportunity>{ new Opportunity(Amount = 1), new Opportunity(Amount = 3), expectedSecondItem, expectedFirstItem };
oppsToSort.sort(sorter);

System.assert(true, 'Should make it here');
}

@IsTest
static void sortsFieldNames() {
List<RollupCalculator.WinnowResult> itemsToSort = new List<RollupCalculator.WinnowResult>{
new RollupCalculator.WinnowResult(new Opportunity(StageName = 'Two')),
new RollupCalculator.WinnowResult(new Opportunity(StageName = 'Uno Reverse Card')),
new RollupCalculator.WinnowResult(new Opportunity(StageName = 'Two')),
new RollupCalculator.WinnowResult(new Opportunity()),
new RollupCalculator.WinnowResult(new Opportunity(StageName = 'Z')),
new RollupCalculator.WinnowResult(new Opportunity(StageName = 'One')),
new RollupCalculator.WinnowResult(new Opportunity(StageName = 'One'))
List<Opportunity> itemsToSort = new List<Opportunity>{
new Opportunity(StageName = 'Two'),
new Opportunity(StageName = 'Uno Reverse Card'),
new Opportunity(StageName = 'Two'),
new Opportunity(),
new Opportunity(StageName = 'Z'),
new Opportunity(StageName = 'One'),
new Opportunity(StageName = 'One')
};

itemsToSort.sort(new RollupCalcItemSorter(new List<String>{ Opportunity.Name.getDescribe().getName(), Opportunity.StageName.getDescribe().getName() }));

System.assertEquals(null, itemsToSort.get(0).item.get(Opportunity.StageName));
System.assertEquals('One', itemsToSort.get(1).item.get(Opportunity.StageName));
System.assertEquals('One', itemsToSort.get(2).item.get(Opportunity.StageName));
System.assertEquals('Two', itemsToSort.get(3).item.get(Opportunity.StageName));
System.assertEquals('Two', itemsToSort.get(4).item.get(Opportunity.StageName));
System.assertEquals('Uno Reverse Card', itemsToSort.get(5).item.get(Opportunity.StageName));
System.assertEquals('Z', itemsToSort.get(6).item.get(Opportunity.StageName));
System.assertEquals(null, itemsToSort.get(0).StageName);
System.assertEquals('One', itemsToSort.get(1).StageName);
System.assertEquals('One', itemsToSort.get(2).StageName);
System.assertEquals('Two', itemsToSort.get(3).StageName);
System.assertEquals('Two', itemsToSort.get(4).StageName);
System.assertEquals('Uno Reverse Card', itemsToSort.get(5).StageName);
System.assertEquals('Z', itemsToSort.get(6).StageName);
}
}
Loading

0 comments on commit 8fa1867

Please sign in to comment.