Skip to content

Commit c367983

Browse files
authored
v1.6.17 - Adds CMDT support for Task/User/Event (#570)
* Upgrading @jongpie's sfdx-bummer-plugin dep to account for updated sf config syntax * adding logging for deferred parent updates, consolidated some sorting logic * Updating prettier * Fixes an issue found while investigating #568 where multiple batch full recalcs to the same parent would not get properly chained together as cabooses * Fixes #568 by allowing full recalc flows to start up from previously unsupported objects like Task/Event/User. Still need to add the corresponding validation rules to ensure the correct rollup fields are filled out, but otherwise this one is all set
1 parent 665cec0 commit c367983

35 files changed

+2261
-471
lines changed

README.md

Lines changed: 5 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -24,12 +24,12 @@ As well, don't miss [the Wiki](../../wiki), which includes even more info for co
2424

2525
## Deployment & Setup
2626

27-
<a href="https://login.salesforce.com/packaging/installPackage.apexp?p0=04t6g000008OaZVAA0">
27+
<a href="https://login.salesforce.com/packaging/installPackage.apexp?p0=04t6g000008OafFAAS">
2828
<img alt="Deploy to Salesforce"
2929
src="./media/deploy-package-to-prod.png">
3030
</a>
3131

32-
<a href="https://test.salesforce.com/packaging/installPackage.apexp?p0=04t6g000008OaZVAA0">
32+
<a href="https://test.salesforce.com/packaging/installPackage.apexp?p0=04t6g000008OafFAAS">
3333
<img alt="Deploy to Salesforce Sandbox"
3434
src="./media/deploy-package-to-sandbox.png">
3535
</a>
@@ -77,7 +77,7 @@ You have several different options when it comes to making use of Apex Rollup:
7777

7878
## CMDT-based Rollup Solution
7979

80-
Custom Metadata Type (CMDT) records are the preferred way to configure and organize your rollups. There are some limitations to this approach, particularly for popular objects like Task and Event - expand the section below and navigate to the ["How To Configure Rollups When An Object Does Not Appear In The CMDT Dropdowns"](#taskeventuser-rollups---how-to-configure-rollups-when-an-object-does-not-appear-in-the-cmdt-dropdowns) section.
80+
Custom Metadata Type (CMDT) records are the preferred way to configure and organize your rollups. There are some limitations to this approach, particularly for popular objects like Task and Event - if you don't see the object you'd like to use in either the parent or child dropdown(s), use the "Text" version of that field (eg `Child Object (Text)`).
8181

8282
<details>
8383
<summary>Expand for CMDT-driven info</summary>
@@ -172,13 +172,6 @@ In addition to the above, some other considerations when it comes to the where c
172172
- Any time a polymorphic field is used in your `Child Object Where Clause`, you must also have a constraint on the parent-level `Type` in order for it to work. If you are filtering on `Task.What`, for example, you must have only a single SObject-parent type as part of your where clause, e.g. `What.Name = 'someName' AND What.Type = 'Account'`.
173173
- for rollups set up against objects with Large Data Volume (LDV - typically when the number of records for a given object exceed 300k), please note that full recalculations (either through the `REFRESH` context in Flow or through the `Recalculate Rollups` tab) are subject to the same query limits that exist elsewhere with SOQL; namely, that filtering on non-indexed fields can cause the initial batch recalculation process to timeout. If you receive an error with the message `REQUEST_RUNNING_TOO_LONG`, it's likely you're trying to roll values up using a where clause with non-indexed fields. Try changing your where clause to use indexed fields, or contact Salesforce Support to have a custom index created (as of Winter '23, you can also create custom indexes programmatically through the CLI, but I would only recommend this option to advanced users)
174174

175-
#### Task/Event/User Rollups - How To Configure Rollups When An Object Does Not Appear In The CMDT Dropdowns
176-
177-
There are several limitations to Entity Definition relationships when using Custom Metadata Types - most notably that they don't work with all objects. User, Task, Event, Case Comment, and others are excluded from the dropdowns generated by Entity Definition fields. However, you still have options when it comes to creating Rollups where these unsupported objects are the children or parent. Note that for both options, I've included YouTube links -.
178-
179-
1. You can use the base Invocable action - the one titled `Perform rollup on records` - as that one allows you to enter the values for the child/parent with text. [Here's an example of me filling out the base action on YouTube](https://youtu.be/jZy4gUKjw3Q?t=1133). If an object supports Record Triggered Flows but not the CMDT dropdowns, this is the perfect way to quickly get up and running
180-
2. You can create simple Apex triggers - [I show off how to do this on YouTube](https://www.youtube.com/watch?v=RyQHXi5boW0&t=839s). That link will forward you to the exact timestamp where the Task/Event explanation begins, but the example is broadly applicable to any object that isn't supported via the built-in dropdowns
181-
182175
</details>
183176

184177
### Configure Custom Orderings With Rollup Order By Metadata
@@ -345,7 +338,7 @@ There is an included Lightning Web Component (LWC) that will show up in the "Cus
345338

346339
**Special notes on the recalc button**
347340

348-
- It relies on your rollups being configured using the `Rollup__mdt` CMDT. Unfortunately this means that it won't work for User/Task/Event-based rollups, or Rollups that are configured via the base Invocable Action (which uses text fields instead of `Rollup__mdt` records)
341+
- It relies on your rollups being configured using the `Rollup__mdt` CMDT. Unfortunately this means that it won't work for Rollups that are configured via the base Invocable Action
349342
- The button will not display on the flexipage at all until at least one `Rollup__mdt.LookupObject__c` field matches the SObject whose record flexipage you're dropping the button on
350343
- The button _will_ display even if a given parent record has no matching children associated with the rollup(s) in question.
351344
- This particular rollup runs synchronously, so it won't eat into your Asynchronous Job limits for the day; it also refreshes any Aura/page-layout sections of the page (LWC-based sections of the page should update automatically).
@@ -460,8 +453,7 @@ global static void runFromCDCTrigger()
460453
global static void runFromApex(List<SObject> calcItems, TriggerOperation rollupContext)
461454

462455
// for more info on how this method differs from the one above it, check out the "Parent Level Merges" section!
463-
// for anything OTHER than merge situations or rollups starting from Task, Event, or User, use of this method
464-
// is an anti-pattern
456+
// for anything OTHER than merge situations use of this method is an anti-pattern
465457
global static Rollup runFromApex(List<Rollup__mdt> rollupMetadata, Evaluator eval, List<SObject> calcItems, Map<Id, SObject> oldCalcItems)
466458

467459
// imperatively from Apex with arguments taking the place of values previously supplied by CMDT
@@ -945,41 +937,6 @@ trigger ContactTrigger on Contact(after delete) {
945937
}
946938
```
947939

948-
If you are using record-triggered flows (or the invocable actions in general), _and_ your child records are targeting Task, Event, or User, this is one area you'll still need to conform to the above with some special caveats. While it's true that Custom Metadata `Rollup__mdt` records can't be created for these three objects, that doesn't mean those very same records can't be synthetically created in Apex (or that the globally exposed Apex rollup methods can't be used on those objects). To that effect, your corresponding `ContactTrigger` (or after delete method within your trigger handler class, since hopefully we're all using those ...) would look something like this:
949-
950-
```java
951-
trigger ContactTrigger on Contact(after delete) {
952-
// each of these Rollups should directly correspond to the equivalent invocable Rollup action
953-
// this is because merge-related rollups will bypass your record-triggered flows and do the work
954-
// directly within the Apex trigger. If you aren't operating on Task/Event/User as the child object,
955-
// set up your rollups using CMDT and use "Rollup.runFromTrigger();" instead!
956-
Rollup.batch(
957-
Rollup.firstFromApex(
958-
Task.Subject,
959-
Task.WhoId,
960-
Contact.Id,
961-
Contact.Description,
962-
Contact.SObjectType,
963-
null, // default recalc value
964-
new List<RollupOrderBy__mdt>{
965-
new RollupOrderBy__mdt(
966-
FieldName__c = 'ActivityDate',
967-
Ranking__c = 0
968-
)
969-
},
970-
RollupEvaluator.getWhereEval('Subject = \'Hello world!\'', Task.SObjectType)
971-
),
972-
Rollup.concatDistinctFromApex(
973-
Event.Subject,
974-
Event.WhoId,
975-
Contact.Id,
976-
Contact.Description,
977-
Contact.SObjectType
978-
)
979-
);
980-
}
981-
```
982-
983940
### Change Data Capture (CDC)
984941

985942
As of [v1.0.4](https://github.com/jamessimone/apex-rollup/releases/tag/v1.0.4), CDC _is_ supported. However, at the moment Change Data Capture can be used strictly through CMDT, and requires a different one-liner for installation into your CDC object Trigger:

extra-tests/classes/RollupFlowFullRecalcTests.cls

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -374,6 +374,43 @@ private class RollupFlowFullRecalcTests {
374374
System.assertEquals(secondParent.AnnualRevenue, updatedSecondParent.AnnualRevenue, 'Out of scope parent values should not have been affected');
375375
}
376376

377+
@IsTest
378+
static void shouldRefreshMultipleBatchFullRecalcs() {
379+
Rollup.defaultControl = new RollupControl__mdt(MaxLookupRowsBeforeBatching__c = 1, IsRollupLoggingEnabled__c = true, MaxRollupRetries__c = 3);
380+
Account acc = [SELECT Id FROM Account];
381+
Individual secondParent = new Individual(LastName = 'Second');
382+
insert secondParent;
383+
List<ContactPointAddress> childrenToInsert = new List<ContactPointAddress>{
384+
new ContactPointAddress(PreferenceRank = 500, ParentId = acc.Id, Name = 'A1'),
385+
new ContactPointAddress(PreferenceRank = 500, ParentId = acc.Id, Name = 'A2'),
386+
new ContactPointAddress(PreferenceRank = 250, ParentId = secondParent.Id, Name = 'B1'),
387+
new ContactPointAddress(PreferenceRank = 250, ParentId = secondParent.Id, Name = 'B2')
388+
};
389+
insert childrenToInsert;
390+
391+
List<ContactPointAddress> cpas = new List<ContactPointAddress>{ childrenToInsert[0], childrenToInsert[2] };
392+
393+
List<Rollup.FlowInput> flowInputs = RollupTestUtils.prepareFlowTest(cpas, 'REFRESH', 'SUM');
394+
flowInputs[0].deferProcessing = true;
395+
flowInputs[0].calcItemWhereClause = 'PreferenceRank > 0';
396+
List<Rollup.FlowInput> secondInputs = RollupTestUtils.prepareFlowTest(cpas, 'REFRESH', 'SUM');
397+
secondInputs[0].rollupSObjectName = 'Individual';
398+
secondInputs[0].rollupFieldOnOpObject = 'ConsumerCreditScore';
399+
secondInputs[0].rollupOperation = 'SUM';
400+
secondInputs[0].deferProcessing = true;
401+
402+
Test.startTest();
403+
Rollup.performRollup(flowInputs);
404+
Rollup.performRollup(secondInputs);
405+
Rollup.processStoredFlowRollups();
406+
Test.stopTest();
407+
408+
Account updatedAcc = [SELECT Id, AnnualRevenue FROM Account WHERE Id = :acc.Id];
409+
System.assertEquals(1000, updatedAcc.AnnualRevenue);
410+
Individual updatedSecondParent = [SELECT Id, ConsumerCreditScore FROM Individual WHERE Id = :secondParent.Id];
411+
System.assertEquals(500, updatedSecondParent.ConsumerCreditScore);
412+
}
413+
377414
@IsTest
378415
static void shouldCorrectlyFilterParentFieldsFromFlowChildren() {
379416
Account acc = [SELECT Id, Name FROM Account];

extra-tests/customMetadata/Rollup.ParentWhereClauseFromTrigger.md-meta.xml

Lines changed: 70 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,17 @@
22
<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">
33
<label>ParentWhereClauseFromTrigger</label>
44
<protected>false</protected>
5+
<values>
6+
<field>CalcItemText__c</field>
7+
<value xsi:type="xsd:string">Application__c</value>
8+
</values>
59
<values>
610
<field>CalcItemWhereClause__c</field>
711
<value xsi:type="xsd:string">ParentApplication__r.Name = &apos;Parent&apos;</value>
812
</values>
913
<values>
1014
<field>CalcItem__c</field>
11-
<value xsi:type="xsd:string">Application__c</value>
15+
<value xsi:nil="true"/>
1216
</values>
1317
<values>
1418
<field>ChangedFieldsOnCalcItem__c</field>
@@ -18,6 +22,14 @@
1822
<field>ConcatDelimiter__c</field>
1923
<value xsi:nil="true"/>
2024
</values>
25+
<values>
26+
<field>CurrencyFieldMapping__c</field>
27+
<value xsi:nil="true"/>
28+
</values>
29+
<values>
30+
<field>Description__c</field>
31+
<value xsi:nil="true"/>
32+
</values>
2133
<values>
2234
<field>FullRecalculationDefaultNumberValue__c</field>
2335
<value xsi:nil="true"/>
@@ -30,6 +42,18 @@
3042
<field>GrandparentRelationshipFieldPath__c</field>
3143
<value xsi:nil="true"/>
3244
</values>
45+
<values>
46+
<field>GroupByFields__c</field>
47+
<value xsi:nil="true"/>
48+
</values>
49+
<values>
50+
<field>GroupByRowEndDelimiter__c</field>
51+
<value xsi:nil="true"/>
52+
</values>
53+
<values>
54+
<field>GroupByRowStartDelimiter__c</field>
55+
<value xsi:nil="true"/>
56+
</values>
3357
<values>
3458
<field>IsFullRecordSet__c</field>
3559
<value xsi:type="xsd:boolean">false</value>
@@ -39,17 +63,41 @@
3963
<value xsi:type="xsd:boolean">false</value>
4064
</values>
4165
<values>
42-
<field>LookupFieldOnCalcItem__c</field>
66+
<field>IsTableFormatted__c</field>
67+
<value xsi:type="xsd:boolean">false</value>
68+
</values>
69+
<values>
70+
<field>LimitAmount__c</field>
71+
<value xsi:nil="true"/>
72+
</values>
73+
<values>
74+
<field>LookupFieldOnCalcItemText__c</field>
4375
<value xsi:type="xsd:string">ParentApplication__c</value>
4476
</values>
4577
<values>
46-
<field>LookupFieldOnLookupObject__c</field>
78+
<field>LookupFieldOnCalcItem__c</field>
79+
<value xsi:nil="true"/>
80+
</values>
81+
<values>
82+
<field>LookupFieldOnLookupObjectText__c</field>
4783
<value xsi:type="xsd:string">Id</value>
4884
</values>
4985
<values>
50-
<field>LookupObject__c</field>
86+
<field>LookupFieldOnLookupObject__c</field>
87+
<value xsi:nil="true"/>
88+
</values>
89+
<values>
90+
<field>LookupObjectText__c</field>
5191
<value xsi:type="xsd:string">ParentApplication__c</value>
5292
</values>
93+
<values>
94+
<field>LookupObject__c</field>
95+
<value xsi:nil="true"/>
96+
</values>
97+
<values>
98+
<field>OneToManyGrandparentFields__c</field>
99+
<value xsi:nil="true"/>
100+
</values>
53101
<values>
54102
<field>OrderByFirstLast__c</field>
55103
<value xsi:nil="true"/>
@@ -59,13 +107,21 @@
59107
<value xsi:type="xsd:string">Org_Defaults</value>
60108
</values>
61109
<values>
62-
<field>RollupFieldOnCalcItem__c</field>
110+
<field>RollupFieldOnCalcItemText__c</field>
63111
<value xsi:type="xsd:string">Picklist__c</value>
64112
</values>
65113
<values>
66-
<field>RollupFieldOnLookupObject__c</field>
114+
<field>RollupFieldOnCalcItem__c</field>
115+
<value xsi:nil="true"/>
116+
</values>
117+
<values>
118+
<field>RollupFieldOnLookupObjectText__c</field>
67119
<value xsi:type="xsd:string">Name</value>
68120
</values>
121+
<values>
122+
<field>RollupFieldOnLookupObject__c</field>
123+
<value xsi:nil="true"/>
124+
</values>
69125
<values>
70126
<field>RollupOperation__c</field>
71127
<value xsi:type="xsd:string">FIRST</value>
@@ -74,6 +130,14 @@
74130
<field>RollupToUltimateParent__c</field>
75131
<value xsi:type="xsd:boolean">false</value>
76132
</values>
133+
<values>
134+
<field>SharingMode__c</field>
135+
<value xsi:nil="true"/>
136+
</values>
137+
<values>
138+
<field>ShouldRunWithoutCustomSettingEnabled__c</field>
139+
<value xsi:type="xsd:boolean">false</value>
140+
</values>
77141
<values>
78142
<field>SplitConcatDelimiterOnCalcItem__c</field>
79143
<value xsi:type="xsd:boolean">false</value>

0 commit comments

Comments
 (0)