Skip to content

Commit e1a00a6

Browse files
authored
v1.7.8 - RollupCalcItemReplacer bugfix (#661)
* Fixes #660 by ensuring calls to split don't accidentally greedily split when detecting whether or not RollupCalcItemReplacer needs to run
1 parent 399ed0f commit e1a00a6

File tree

10 files changed

+87
-36
lines changed

10 files changed

+87
-36
lines changed

README.md

+2-2
Original file line numberDiff line numberDiff line change
@@ -24,11 +24,11 @@ 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=04t6g000008OfrlAAC">
27+
<a href="https://login.salesforce.com/packaging/installPackage.apexp?p0=04t6g000008OfrvAAC">
2828
<img alt="Deploy to Salesforce" src="./media/deploy-package-to-prod.png">
2929
</a>
3030

31-
<a href="https://test.salesforce.com/packaging/installPackage.apexp?p0=04t6g000008OfrlAAC">
31+
<a href="https://test.salesforce.com/packaging/installPackage.apexp?p0=04t6g000008OfrvAAC">
3232
<img alt="Deploy to Salesforce Sandbox" src="./media/deploy-package-to-sandbox.png">
3333
</a>
3434
<br/>

extra-tests/classes/RollupCalcItemReplacerTests.cls

+18
Original file line numberDiff line numberDiff line change
@@ -218,6 +218,7 @@ private class RollupCalcItemReplacerTests {
218218
@IsTest
219219
static void doesNotReplaceForCustomTypeFields() {
220220
List<Account> accounts = [SELECT Id FROM Account];
221+
Integer currentQueryCount = Limits.getQueries();
221222
RollupCalcItemReplacer replacer = new RollupCalcItemReplacer(
222223
new RollupControl__mdt(IsRollupLoggingEnabled__c = true, ReplaceCalcItemsAsyncWhenOverCount__c = 3)
223224
);
@@ -227,5 +228,22 @@ private class RollupCalcItemReplacerTests {
227228

228229
Assert.areEqual(1, records.size());
229230
Assert.isTrue(replacer.hasProcessedMetadata(metas, accounts));
231+
Assert.areEqual(currentQueryCount, Limits.getQueries());
232+
}
233+
234+
@IsTest
235+
static void doesNotReplaceForCustomTypeFieldsWithPrecedingCharacters() {
236+
List<Account> accounts = [SELECT Id, AType__c FROM Account];
237+
Integer currentQueryCount = Limits.getQueries();
238+
RollupCalcItemReplacer replacer = new RollupCalcItemReplacer(
239+
new RollupControl__mdt(IsRollupLoggingEnabled__c = true, ReplaceCalcItemsAsyncWhenOverCount__c = 3)
240+
);
241+
List<Rollup__mdt> metas = new List<Rollup__mdt>{ new Rollup__mdt(CalcItemWhereClause__c = 'AType__c = \'hello\'', CalcItem__c = 'Account') };
242+
243+
List<SObject> records = replacer.replace(accounts, metas);
244+
245+
Assert.areEqual(1, records.size());
246+
Assert.isTrue(replacer.hasProcessedMetadata(metas, accounts));
247+
Assert.areEqual(currentQueryCount, Limits.getQueries());
230248
}
231249
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
<?xml version="1.0" encoding="UTF-8" ?>
2+
<CustomField xmlns="http://soap.sforce.com/2006/04/metadata">
3+
<fullName>AType__c</fullName>
4+
<externalId>false</externalId>
5+
<label>Type With Another Character Before It</label>
6+
<length>255</length>
7+
<required>false</required>
8+
<trackTrending>false</trackTrending>
9+
<type>Text</type>
10+
<unique>false</unique>
11+
</CustomField>

package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "apex-rollup",
3-
"version": "1.7.7",
3+
"version": "1.7.8",
44
"description": "Fast, configurable, elastically scaling custom rollup solution. Apex Invocable action, one-liner Apex trigger/CMDT-driven logic, and scheduled Apex-ready.",
55
"repository": {
66
"type": "git",

rollup-namespaced/README.md

+2-2
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,12 @@ For more info, see the base `README`.
1818

1919
## Deployment & Setup
2020

21-
<a href="https://login.salesforce.com/packaging/installPackage.apexp?p0=04t6g000008OfrqAAC">
21+
<a href="https://login.salesforce.com/packaging/installPackage.apexp?p0=04t6g000008Ofs0AAC">
2222
<img alt="Deploy to Salesforce"
2323
src="./media/deploy-package-to-prod.png">
2424
</a>
2525

26-
<a href="https://test.salesforce.com/packaging/installPackage.apexp?p0=04t6g000008OfrqAAC">
26+
<a href="https://test.salesforce.com/packaging/installPackage.apexp?p0=04t6g000008Ofs0AAC">
2727
<img alt="Deploy to Salesforce Sandbox"
2828
src="./media/deploy-package-to-sandbox.png">
2929
</a>

rollup-namespaced/sfdx-project.json

+4-3
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@
44
"default": true,
55
"package": "apex-rollup-namespaced",
66
"path": "rollup-namespaced/source/rollup",
7-
"versionName": "More multi-currency bugfixes",
8-
"versionNumber": "1.2.6.0",
7+
"versionName": "Properly handle namespaced and other Type__c fields in RollupCalcItemReplacer",
8+
"versionNumber": "1.2.7.0",
99
"versionDescription": "Fast, configurable, elastically scaling custom rollup solution. Apex Invocable action, one-liner Apex trigger/CMDT-driven logic, and scheduled Apex-ready.",
1010
"releaseNotesUrl": "https://github.com/jamessimone/apex-rollup/releases/latest",
1111
"unpackagedMetadata": {
@@ -31,6 +31,7 @@
3131
"apex-rollup-namespaced@1.2.3": "04t6g000008Off4AAC",
3232
"apex-rollup-namespaced@1.2.4": "04t6g000008OfqYAAS",
3333
"apex-rollup-namespaced@1.2.5": "04t6g000008OfrCAAS",
34-
"apex-rollup-namespaced@1.2.6": "04t6g000008OfrqAAC"
34+
"apex-rollup-namespaced@1.2.6": "04t6g000008OfrqAAC",
35+
"apex-rollup-namespaced@1.2.7": "04t6g000008Ofs0AAC"
3536
}
3637
}

rollup/core/classes/RollupCalcItemReplacer.cls

+16-14
Original file line numberDiff line numberDiff line change
@@ -150,20 +150,21 @@ public without sharing class RollupCalcItemReplacer {
150150
if (calcItems.isEmpty()) {
151151
return calcItems;
152152
}
153-
Map<Id, SObject> idToCalcItem = RollupFieldInitializer.Current.createSafeMap(calcItems);
154153
Set<String> baseFields = this.baseQueryFields.get('' + calcItems[0].getSObjectType());
155-
if (baseFields?.isEmpty() == false) {
156-
SObject firstItem = calcItems[0];
157-
String queryString = RollupQueryBuilder.Current.getQuery(firstItem.getSObjectType(), new List<String>(baseFields), 'Id', '=');
158-
List<SObject> calcItemsWithReplacement = this.repo.setQuery(queryString).setArg(calcItems).get();
159-
Map<String, Schema.SObjectField> fieldNameToDescribe = firstItem.getSObjectType().getDescribe().fields.getMap();
160-
for (SObject calcItemWithReplacement : calcItemsWithReplacement) {
161-
if (idToCalcItem.containsKey(calcItemWithReplacement.Id)) {
162-
SObject calcItem = idToCalcItem.get(calcItemWithReplacement.Id);
163-
for (String baseField : baseFields) {
164-
Schema.SObjectfield baseFieldToken = fieldNameToDescribe.get(baseField);
165-
idToCalcItem.put(calcItem.Id, replaceField(calcItem, baseFieldToken, calcItemWithReplacement.get(baseField)));
166-
}
154+
if (baseFields?.isEmpty() != false) {
155+
return calcItems;
156+
}
157+
Map<Id, SObject> idToCalcItem = RollupFieldInitializer.Current.createSafeMap(calcItems);
158+
SObject firstItem = calcItems[0];
159+
String queryString = RollupQueryBuilder.Current.getQuery(firstItem.getSObjectType(), new List<String>(baseFields), 'Id', '=');
160+
List<SObject> calcItemsWithReplacement = this.repo.setQuery(queryString).setArg(calcItems).get();
161+
Map<String, Schema.SObjectField> fieldNameToDescribe = firstItem.getSObjectType().getDescribe().fields.getMap();
162+
for (SObject calcItemWithReplacement : calcItemsWithReplacement) {
163+
if (idToCalcItem.containsKey(calcItemWithReplacement.Id)) {
164+
SObject calcItem = idToCalcItem.get(calcItemWithReplacement.Id);
165+
for (String baseField : baseFields) {
166+
Schema.SObjectfield baseFieldToken = fieldNameToDescribe.get(baseField);
167+
idToCalcItem.put(calcItem.Id, replaceField(calcItem, baseFieldToken, calcItemWithReplacement.get(baseField)));
167168
}
168169
}
169170
}
@@ -249,7 +250,8 @@ public without sharing class RollupCalcItemReplacer {
249250

250251
private void processWhereClauseForDownstreamEvals(Schema.SObjectType sObjectType, Rollup__mdt metadata, RollupEvaluator.WhereFieldEvaluator whereEval) {
251252
for (String whereClause : whereEval.getWhereClauses()) {
252-
Boolean hasTypeField = whereClause.split(TYPE_FIELD + '(?!__r)').size() > 1;
253+
// the period needs to be escaped when splitting, otherwise this can match namespaced fields accidentally
254+
Boolean hasTypeField = whereClause.split('\\' + TYPE_FIELD + '(?!__r)').size() > 1;
253255
Boolean hasOwnerField = whereClause.contains(OWNER);
254256
if (hasTypeField == false && hasOwnerField == false) {
255257
continue;

rollup/core/classes/RollupCurrencyInfo.cls

+28-10
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,36 @@
11
@SuppressWarnings('PMD.ExcessiveParameterList,PMD.PropertyNamingConventions')
22
public without sharing virtual class RollupCurrencyInfo implements RollupLogger.ToStringObject {
33
public static final String CURRENCY_ISO_CODE_FIELD_NAME = 'CurrencyIsoCode';
4-
@TestVisible
5-
private static final RollupRepository REPOSITORY = new RollupRepository(RollupRepository.RunAsMode.SYSTEM_LEVEL);
6-
private static final Map<String, SObject> TRANSFORMED_MULTICURRENCY_CALC_ITEMS = new Map<String, SObject>();
7-
private static final Set<String> HASHED_ITEM_VALUES = new Set<String>();
84

5+
@TestVisible
6+
private static Boolean hasLoadedDatedCurrencyInfo = false;
97
@TestVisible
108
private static List<RollupCurrencyInfo> mockBasicCurrencies;
119
@TestVisible
1210
private static List<RollupCurrencyInfo> mockDatedCurrencies;
1311

1412
@TestVisible
15-
private static Boolean hasLoadedDatedCurrencyInfo = false;
13+
private static RollupRepository REPOSITORY {
14+
get {
15+
REPOSITORY = REPOSITORY ?? new RollupRepository(RollupRepository.RunAsMode.SYSTEM_LEVEL);
16+
return REPOSITORY;
17+
}
18+
set;
19+
}
20+
private static Map<String, SObject> TRANSFORMED_MULTICURRENCY_CALC_ITEMS {
21+
get {
22+
TRANSFORMED_MULTICURRENCY_CALC_ITEMS = TRANSFORMED_MULTICURRENCY_CALC_ITEMS ?? new Map<String, SObject>();
23+
return TRANSFORMED_MULTICURRENCY_CALC_ITEMS;
24+
}
25+
set;
26+
}
27+
private static Set<String> HASHED_ITEM_VALUES {
28+
get {
29+
HASHED_ITEM_VALUES = HASHED_ITEM_VALUES ?? new Set<String>();
30+
return HASHED_ITEM_VALUES;
31+
}
32+
set;
33+
}
1634

1735
private static Date minDatedCurrencyLookup {
1836
get {
@@ -52,7 +70,7 @@ public without sharing virtual class RollupCurrencyInfo implements RollupLogger.
5270
// Can't be Schema.SObjectType => Schema.SObjectField because not all orgs have OppLineItems/Splits
5371
// technically there's a hierarchy for OpportunityLineItem that goes Opportunity.CloseDate > ServiceDate > (ProductDate || ScheduleDate)
5472
// but because this can be configured on a per-rollup basis, it's fine to leave Opportunity.CloseDate as the default since it can be overridden
55-
private static final Map<String, List<String>> DATED_MULTICURRENCY_SUPPORTED_OBJECTS {
73+
private static Map<String, List<String>> DATED_MULTICURRENCY_SUPPORTED_OBJECTS {
5674
get {
5775
DATED_MULTICURRENCY_SUPPORTED_OBJECTS = DATED_MULTICURRENCY_SUPPORTED_OBJECTS ??
5876
new Map<String, List<String>>{
@@ -66,7 +84,7 @@ public without sharing virtual class RollupCurrencyInfo implements RollupLogger.
6684
set;
6785
}
6886

69-
private static final Set<String> DATED_CURRENCY_QUERIES {
87+
private static Set<String> DATED_CURRENCY_QUERIES {
7088
get {
7189
DATED_CURRENCY_QUERIES = DATED_CURRENCY_QUERIES ?? new Set<String>();
7290
return DATED_CURRENCY_QUERIES;
@@ -83,23 +101,23 @@ public without sharing virtual class RollupCurrencyInfo implements RollupLogger.
83101
set;
84102
}
85103

86-
private static final RollupCurrencyInfo FALLBACK_INFO {
104+
private static RollupCurrencyInfo FALLBACK_INFO {
87105
get {
88106
FALLBACK_INFO = FALLBACK_INFO ?? new RollupCurrencyInfo().setDefaults(1);
89107
return FALLBACK_INFO;
90108
}
91109
set;
92110
}
93111

94-
private static final Map<String, RollupCurrencyInfo> CURRENCY_ISO_CODE_TO_CURRENCY {
112+
private static Map<String, RollupCurrencyInfo> CURRENCY_ISO_CODE_TO_CURRENCY {
95113
get {
96114
CURRENCY_ISO_CODE_TO_CURRENCY = CURRENCY_ISO_CODE_TO_CURRENCY ?? getCurrencyMap();
97115
return CURRENCY_ISO_CODE_TO_CURRENCY;
98116
}
99117
set;
100118
}
101119

102-
private static final Map<Schema.SObjectType, Map<String, Schema.SObjectField>> TYPE_TO_FIELDS {
120+
private static Map<Schema.SObjectType, Map<String, Schema.SObjectField>> TYPE_TO_FIELDS {
103121
get {
104122
TYPE_TO_FIELDS = TYPE_TO_FIELDS ?? new Map<Schema.SObjectType, Map<String, Schema.SObjectField>>();
105123
return TYPE_TO_FIELDS;

rollup/core/classes/RollupLogger.cls

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
global without sharing virtual class RollupLogger implements ILogger {
33
@TestVisible
44
// this gets updated via the pipeline as the version number gets incremented
5-
private static final String CURRENT_VERSION_NUMBER = 'v1.7.7';
5+
private static final String CURRENT_VERSION_NUMBER = 'v1.7.8';
66
private static final System.LoggingLevel FALLBACK_LOGGING_LEVEL = System.LoggingLevel.DEBUG;
77
private static final RollupPlugin PLUGIN = new RollupPlugin();
88

sfdx-project.json

+4-3
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@
55
"package": "apex-rollup",
66
"path": "rollup",
77
"scopeProfiles": true,
8-
"versionName": "More multi-currency bugfixes",
9-
"versionNumber": "1.7.7.0",
8+
"versionName": "Properly handle namespaced and other Type__c fields in RollupCalcItemReplacer",
9+
"versionNumber": "1.7.8.0",
1010
"versionDescription": "Fast, configurable, elastically scaling custom rollup solution. Apex Invocable action, one-liner Apex trigger/CMDT-driven logic, and scheduled Apex-ready.",
1111
"releaseNotesUrl": "https://github.com/jamessimone/apex-rollup/releases/latest",
1212
"unpackagedMetadata": {
@@ -104,6 +104,7 @@
104104
"apex-rollup": "0Ho6g000000TNcOCAW",
105105
"apex-rollup@1.7.5": "04t6g000008OfqTAAS",
106106
"apex-rollup@1.7.6": "04t6g000008Ofr7AAC",
107-
"apex-rollup@1.7.7": "04t6g000008OfrlAAC"
107+
"apex-rollup@1.7.7": "04t6g000008OfrlAAC",
108+
"apex-rollup@1.7.8": "04t6g000008OfrvAAC"
108109
}
109110
}

0 commit comments

Comments
 (0)