Skip to content

Commit 89e90bb

Browse files
jamessimonejongpie
andauthored
V1.2.14 - DLRS rule conversion, recursion detection (#91)
* Fix for #87 - integration tests for trigger-based rollups. Also opens up the potential for future recursion management within Rollup * Added Apex script to convert DLRS rules (#89) * Final notes on #84, fixes #90 by updating README to include info on Concat Delimiter argument * Added RecursiveUpdateEvaluator to prevent recursive rollup updates * Missed some required files when committing RollupChild__c/RollupParent__c. Fixed a few broken tests, attributed @jongpie in Readme for fixing #84, and bumped package version Co-authored-by: Jonathan Gillespie <[email protected]>
1 parent 66ec7b3 commit 89e90bb

25 files changed

+1327
-59
lines changed

README.md

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,12 @@ Create fast, scalable custom rollups driven by Custom Metadata in your Salesforc
99

1010
### Package deployment options
1111

12-
<a href="https://login.salesforce.com/packaging/installPackage.apexp?p0=04t6g000008GJeUAAW">
12+
<a href="https://login.salesforce.com/packaging/installPackage.apexp?p0=04t6g000008GJjQAAW">
1313
<img alt="Deploy to Salesforce"
1414
src="./media/deploy-package-to-prod.png">
1515
</a>
1616

17-
<a href="https://test.salesforce.com/packaging/installPackage.apexp?p0=04t6g000008GJeUAAW">
17+
<a href="https://test.salesforce.com/packaging/installPackage.apexp?p0=04t6g000008GJjQAAW">
1818
<img alt="Deploy to Salesforce"
1919
src="./media/deploy-package-to-sandbox.png">
2020
</a>
@@ -43,6 +43,8 @@ While you can still enable/disable individual rollups from running with the use
4343
- ease of installation/upgrade. Previously some users had issues when installing/upgrading due to pre-existing automation in their orgs interfering with the `Rollup` tests
4444
- granularity of control. Want to disable rollups from running for a specific user or profile? Easy as pie!
4545

46+
If you are converting from DLRS to Rollup, you can automatically convert all of your DLRS rules using the included Apex script [scripts/convert-dlrs-rules.apex](scripts/convert-dlrs-rules.apex). Simply run this script in your org, and all DLRS rules (stored in `dlrs__LookupRollupSummary2__mdt`) will be converted to `Rollup__mdt` records and automatically deployed to the current org. Please note that this script does not delete the existing DLRS rules, nor does it uninstall DLRS for you - after running it, you'll still have to clean up and remove DLRS from your org.
47+
4648
## Usage
4749

4850
You have several different options when it comes to making use of `Rollup`:
@@ -108,8 +110,9 @@ Within the `Rollup__mdt` custom metadata type, add a new record with fields:
108110
- `Lookup Field On Calc Item`- the field storing the Id or String referencing a unique value on another object (In the example, Id)
109111
- `Lookup Field On Lookup Object` - the field on the lookup object that matches the value stored in `Lookup Field On Calc Item`
110112
- `Rollup Field On Lookup Object` - the field on the lookup object where the rolled-up values will be stored (I've been using AnnualRevenue on the account as an example)
111-
- `Rollup Operation` - A picklist field to select the operation you're looking to perform. Acceptable values are SUM / MIN / MAX / AVERAGE / COUNT / COUNT_DISTINCT / CONCAT / CONCAT_DISTINCT / FIRST / LAST. Both CONCAT and CONCAT_DISTINCT separate values with commas in the rollup field itself.
113+
- `Rollup Operation` - A picklist field to select the operation you're looking to perform. Acceptable values are SUM / MIN / MAX / AVERAGE / COUNT / COUNT_DISTINCT / CONCAT / CONCAT_DISTINCT / FIRST / LAST. 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.
112114
- `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.
115+
- `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!
113116
- `Changed Fields On Calc Item` (optional) - comma-separated list of field API Names to filter items from being used in the rollup calculations unless all the stipulated fields have changed
114117
- `Full Recalculation Default Number Value` (optional) - for some rollup operations (SUM / COUNT-based operations in particular), you may want to start fresh with each batch of calculation items provided. When this value is provided, it is used as the basis for rolling values up to the "parent" record (instead of whatever the pre-existing value for that field on the "parent" is, which is the default behavior). **NB**: it's valid to use this field to override the pre-existing value on the "parent" for number-based fields, _and_ that includes Date / Datetime / Time fields as well. In order to work properly for these three field types, however, the value must be converted into UTC milliseconds. You can do this easily using Anonymous Apex, or a site such as [Current Millis](https://currentmillis.com/).
115118
- `Full Recalculation Default String Value` (optional) - same as `Full Recalculation Default Number Value`, but for String-based fields (including Lookup and Id fields).
@@ -179,7 +182,8 @@ Here are the arguments necessary to invoke `Rollup` from a Flow / Process Builde
179182
- `Lookup Field On Lookup Object` - the API Name of the field on the lookup object that matches the value stored in `Lookup Field On Calc Item`
180183
- `Rollup Field On Lookup Object` - the API Name of the field on the lookup object where the rolled-up values will be stored (I've been using AnnualRevenue on the account as an example)
181184
- `Rollup Context` - INSERT / UPDATE / UPSERT / DELETE. **Special note** - unless you are using a Record-Triggered Flow / After Update Process Builder, you almost assuredly want to simply use the INSERT context (see image below). However, you _would_ use something like UPDATE if, after retrieving records using Get Records in an auto-launched flow, you then looped through your collection and modified fields prior to sending them to `Rollup`. You would only ever use UPSERT for a record-triggered flow triggering on `A record is created or updated`
182-
- `Rollup Operation` - the operation you're looking to perform. Acceptable values are SUM / MIN / MAX / AVERAGE / COUNT / COUNT_DISTINCT / CONCAT / CONCAT_DISTINCT / FIRST / LAST. Both CONCAT and CONCAT_DISTINCT separate values with commas
185+
- `Rollup Operation` - the operation you're looking to perform. Acceptable values are SUM / MIN / MAX / AVERAGE / COUNT / COUNT_DISTINCT / CONCAT / CONCAT_DISTINCT / FIRST / LAST. Both CONCAT and CONCAT_DISTINCT separate values with commas by default, but you can use `Concat Delimiter` to change that.
186+
- `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!
183187
- `Calc item changed fields` (optional) - comma-separated list of field API Names to filter items from being used in the rollup calculations unless all the stipulated fields have changed
184188
- `Full Recalculation Default Number Value` (optional) - for some rollup operations (SUM / COUNT-based operations in particular), you may want to start fresh with each batch of calculation items provided. When this value is provided, it is used as the basis for rolling values up to the "parent" record (instead of whatever the pre-existing value for that field on the "parent" is, which is the default behavior). **NB**: it's valid to use this field to override the pre-existing value on the "parent" for number-based fields, _and_ that includes Date / Datetime / Time fields as well. In order to work properly for these three field types, however, the value must be converted into UTC milliseconds. You can do this easily using Anonymous Apex, or a site such as [Current Millis](https://currentmillis.com/).
185189
- `Full Recalculation Default String Value` (optional) - same as `Full Recalculation Default Number Value`, but for String-based fields (including Lookup and Id fields).
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
@isTest
2+
private class CustomMetadataDrivenTests {
3+
4+
@TestSetup
5+
static void setup() {
6+
upsert new RollupSettings__c(IsEnabled__c = true);
7+
insert new RollupParent__c(Name = 'CustomMetadataDrivenTests');
8+
}
9+
10+
@isTest
11+
static void shouldRollupTextFromChildToParentOnInsert() {
12+
RollupParent__c parent = [SELECT Id FROM RollupParent__c];
13+
// uses FIRST with TextField__c as the Order By field
14+
RollupChild__c childOne = new RollupChild__c(Name = 'Child one', TextField__c = 'a', RollupParent__c = parent.Id);
15+
RollupChild__c childTwo = new RollupChild__c(Name = 'Child two', TextField__c = 'b', RollupParent__c = parent.Id);
16+
17+
Test.startTest();
18+
insert new List<RollupChild__c>{ childOne, childTwo };
19+
Test.stopTest();
20+
21+
parent = [SELECT Id, TextField__c FROM RollupParent__c];
22+
23+
System.assertEquals(childOne.TextField__c, parent.TextField__c);
24+
}
25+
26+
@isTest
27+
static void shouldRollupTextFromChildToParentOnUpdate() {
28+
RollupParent__c parent = [SELECT Id FROM RollupParent__c];
29+
// uses FIRST with TextField__c as the Order By field
30+
RollupChild__c childOne = new RollupChild__c(Name = 'Child one', TextField__c = 'a', RollupParent__c = parent.Id);
31+
RollupChild__c childTwo = new RollupChild__c(Name = 'Child two', TextField__c = 'b', RollupParent__c = parent.Id);
32+
insert new List<RollupChild__c>{ childOne, childTwo };
33+
34+
Test.startTest();
35+
childOne.TextField__c = 'c';
36+
update childOne;
37+
Test.stopTest();
38+
39+
parent = [SELECT Id, TextField__c FROM RollupParent__c];
40+
41+
System.assertEquals(childTwo.TextField__c, parent.TextField__c);
42+
}
43+
44+
@isTest
45+
static void shouldRollupTextFromChildToParentOnDelete() {
46+
RollupParent__c parent = [SELECT Id FROM RollupParent__c];
47+
// uses FIRST with TextField__c as the Order By field
48+
RollupChild__c childOne = new RollupChild__c(Name = 'Child one', TextField__c = 'a', RollupParent__c = parent.Id);
49+
RollupChild__c childTwo = new RollupChild__c(Name = 'Child two', TextField__c = 'b', RollupParent__c = parent.Id);
50+
insert new List<RollupChild__c>{ childOne, childTwo };
51+
52+
Test.startTest();
53+
childOne.TextField__c = 'c';
54+
update childOne;
55+
delete childTwo; // and for my next trick ...
56+
Test.stopTest();
57+
58+
parent = [SELECT Id, TextField__c FROM RollupParent__c];
59+
60+
System.assertEquals(childOne.TextField__c, parent.TextField__c);
61+
}
62+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<ApexClass xmlns="http://soap.sforce.com/2006/04/metadata">
3+
<apiVersion>51.0</apiVersion>
4+
<status>Active</status>
5+
</ApexClass>
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<CustomMetadata xmlns="http://soap.sforce.com/2006/04/metadata" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
3+
<label>RollupIntegrationChildRollupNumber</label>
4+
<protected>false</protected>
5+
<values>
6+
<field>CalcItemWhereClause__c</field>
7+
<value xsi:nil="true"/>
8+
</values>
9+
<values>
10+
<field>CalcItem__c</field>
11+
<value xsi:type="xsd:string">RollupChild__c</value>
12+
</values>
13+
<values>
14+
<field>ChangedFieldsOnCalcItem__c</field>
15+
<value xsi:nil="true"/>
16+
</values>
17+
<values>
18+
<field>ConcatDelimiter__c</field>
19+
<value xsi:nil="true"/>
20+
</values>
21+
<values>
22+
<field>FullRecalculationDefaultNumberValue__c</field>
23+
<value xsi:nil="true"/>
24+
</values>
25+
<values>
26+
<field>FullRecalculationDefaultStringValue__c</field>
27+
<value xsi:nil="true"/>
28+
</values>
29+
<values>
30+
<field>GrandparentRelationshipFieldPath__c</field>
31+
<value xsi:nil="true"/>
32+
</values>
33+
<values>
34+
<field>IsFullRecordSet__c</field>
35+
<value xsi:type="xsd:boolean">false</value>
36+
</values>
37+
<values>
38+
<field>IsRollupStartedFromParent__c</field>
39+
<value xsi:type="xsd:boolean">false</value>
40+
</values>
41+
<values>
42+
<field>LookupFieldOnCalcItem__c</field>
43+
<value xsi:type="xsd:string">RollupParent__c</value>
44+
</values>
45+
<values>
46+
<field>LookupFieldOnLookupObject__c</field>
47+
<value xsi:type="xsd:string">Id</value>
48+
</values>
49+
<values>
50+
<field>LookupObject__c</field>
51+
<value xsi:type="xsd:string">RollupParent__c</value>
52+
</values>
53+
<values>
54+
<field>OrderByFirstLast__c</field>
55+
<value xsi:nil="true"/>
56+
</values>
57+
<values>
58+
<field>RollupControl__c</field>
59+
<value xsi:type="xsd:string">Org_Defaults</value>
60+
</values>
61+
<values>
62+
<field>RollupFieldOnCalcItem__c</field>
63+
<value xsi:type="xsd:string">NumberField__c</value>
64+
</values>
65+
<values>
66+
<field>RollupFieldOnLookupObject__c</field>
67+
<value xsi:type="xsd:string">NumberField__c</value>
68+
</values>
69+
<values>
70+
<field>RollupOperation__c</field>
71+
<value xsi:type="xsd:string">MAX</value>
72+
</values>
73+
<values>
74+
<field>RollupToUltimateParent__c</field>
75+
<value xsi:type="xsd:boolean">false</value>
76+
</values>
77+
<values>
78+
<field>UltimateParentLookup__c</field>
79+
<value xsi:nil="true"/>
80+
</values>
81+
</CustomMetadata>
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<CustomMetadata xmlns="http://soap.sforce.com/2006/04/metadata" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
3+
<label>RollupIntegrationChildRollupText</label>
4+
<protected>false</protected>
5+
<values>
6+
<field>CalcItemWhereClause__c</field>
7+
<value xsi:nil="true"/>
8+
</values>
9+
<values>
10+
<field>CalcItem__c</field>
11+
<value xsi:type="xsd:string">RollupChild__c</value>
12+
</values>
13+
<values>
14+
<field>ChangedFieldsOnCalcItem__c</field>
15+
<value xsi:nil="true"/>
16+
</values>
17+
<values>
18+
<field>ConcatDelimiter__c</field>
19+
<value xsi:nil="true"/>
20+
</values>
21+
<values>
22+
<field>FullRecalculationDefaultNumberValue__c</field>
23+
<value xsi:nil="true"/>
24+
</values>
25+
<values>
26+
<field>FullRecalculationDefaultStringValue__c</field>
27+
<value xsi:nil="true"/>
28+
</values>
29+
<values>
30+
<field>GrandparentRelationshipFieldPath__c</field>
31+
<value xsi:nil="true"/>
32+
</values>
33+
<values>
34+
<field>IsFullRecordSet__c</field>
35+
<value xsi:type="xsd:boolean">false</value>
36+
</values>
37+
<values>
38+
<field>IsRollupStartedFromParent__c</field>
39+
<value xsi:type="xsd:boolean">false</value>
40+
</values>
41+
<values>
42+
<field>LookupFieldOnCalcItem__c</field>
43+
<value xsi:type="xsd:string">RollupParent__c</value>
44+
</values>
45+
<values>
46+
<field>LookupFieldOnLookupObject__c</field>
47+
<value xsi:type="xsd:string">Id</value>
48+
</values>
49+
<values>
50+
<field>LookupObject__c</field>
51+
<value xsi:type="xsd:string">RollupParent__c</value>
52+
</values>
53+
<values>
54+
<field>OrderByFirstLast__c</field>
55+
<value xsi:type="xsd:string">TextField__c</value>
56+
</values>
57+
<values>
58+
<field>RollupControl__c</field>
59+
<value xsi:type="xsd:string">Org_Defaults</value>
60+
</values>
61+
<values>
62+
<field>RollupFieldOnCalcItem__c</field>
63+
<value xsi:type="xsd:string">TextField__c</value>
64+
</values>
65+
<values>
66+
<field>RollupFieldOnLookupObject__c</field>
67+
<value xsi:type="xsd:string">TextField__c</value>
68+
</values>
69+
<values>
70+
<field>RollupOperation__c</field>
71+
<value xsi:type="xsd:string">FIRST</value>
72+
</values>
73+
<values>
74+
<field>RollupToUltimateParent__c</field>
75+
<value xsi:type="xsd:boolean">false</value>
76+
</values>
77+
<values>
78+
<field>UltimateParentLookup__c</field>
79+
<value xsi:nil="true"/>
80+
</values>
81+
</CustomMetadata>
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<Layout xmlns="http://soap.sforce.com/2006/04/metadata">
3+
<layoutSections>
4+
<customLabel>false</customLabel>
5+
<detailHeading>false</detailHeading>
6+
<editHeading>true</editHeading>
7+
<label>Information</label>
8+
<layoutColumns>
9+
<layoutItems>
10+
<behavior>Required</behavior>
11+
<field>Name</field>
12+
</layoutItems>
13+
<layoutItems>
14+
<behavior>Edit</behavior>
15+
<field>RollupParent__c</field>
16+
</layoutItems>
17+
<layoutItems>
18+
<behavior>Edit</behavior>
19+
<field>NumberField__c</field>
20+
</layoutItems>
21+
<layoutItems>
22+
<behavior>Edit</behavior>
23+
<field>TextField__c</field>
24+
</layoutItems>
25+
</layoutColumns>
26+
<layoutColumns>
27+
<layoutItems>
28+
<behavior>Edit</behavior>
29+
<field>OwnerId</field>
30+
</layoutItems>
31+
</layoutColumns>
32+
<style>TwoColumnsTopToBottom</style>
33+
</layoutSections>
34+
<layoutSections>
35+
<customLabel>false</customLabel>
36+
<detailHeading>false</detailHeading>
37+
<editHeading>true</editHeading>
38+
<label>System Information</label>
39+
<layoutColumns>
40+
<layoutItems>
41+
<behavior>Readonly</behavior>
42+
<field>CreatedById</field>
43+
</layoutItems>
44+
</layoutColumns>
45+
<layoutColumns>
46+
<layoutItems>
47+
<behavior>Readonly</behavior>
48+
<field>LastModifiedById</field>
49+
</layoutItems>
50+
</layoutColumns>
51+
<style>TwoColumnsTopToBottom</style>
52+
</layoutSections>
53+
<layoutSections>
54+
<customLabel>false</customLabel>
55+
<detailHeading>false</detailHeading>
56+
<editHeading>true</editHeading>
57+
<layoutColumns/>
58+
<layoutColumns/>
59+
<layoutColumns/>
60+
<style>CustomLinks</style>
61+
</layoutSections>
62+
<showEmailCheckbox>false</showEmailCheckbox>
63+
<showHighlightsPanel>false</showHighlightsPanel>
64+
<showInteractionLogPanel>false</showInteractionLogPanel>
65+
<showRunAssignmentRulesCheckbox>false</showRunAssignmentRulesCheckbox>
66+
<showSubmitAndAttachButton>false</showSubmitAndAttachButton>
67+
</Layout>

0 commit comments

Comments
 (0)