diff --git a/.gitignore b/.gitignore
index 1660d65c..35f52ce6 100644
--- a/.gitignore
+++ b/.gitignore
@@ -20,4 +20,5 @@ sfge-*.log.gz
.DS_Store
.config
.vscode
-config/data/act-pr-event.json
\ No newline at end of file
+config/data/act-pr-event.json
+.sflogsub
\ No newline at end of file
diff --git a/README.md b/README.md
index e8f7e050..581ad92f 100644
--- a/README.md
+++ b/README.md
@@ -24,12 +24,12 @@ As well, don't miss [the Wiki](../../wiki), which includes even more info for co
## Deployment & Setup
-
+
-
+
diff --git a/extra-tests/classes/InvocableDrivenTests.cls b/extra-tests/classes/InvocableDrivenTests.cls
index 122490e9..ddd853f6 100644
--- a/extra-tests/classes/InvocableDrivenTests.cls
+++ b/extra-tests/classes/InvocableDrivenTests.cls
@@ -3,8 +3,7 @@ private class InvocableDrivenTests {
@TestSetup
static void setup() {
upsert new RollupSettings__c(IsEnabled__c = true);
- Account acc = new Account(Name = InvocableDrivenTests.class.getName());
- insert acc;
+ insert new Account(Name = InvocableDrivenTests.class.getName());
}
@IsTest
@@ -182,4 +181,22 @@ private class InvocableDrivenTests {
parent = [SELECT AnnualRevenue FROM Account WHERE Id = :parent.Id];
System.assertEquals(null, parent.AnnualRevenue);
}
+
+ @IsTest
+ static void refreshWorksWithEmptyCollectionsWhenParentIdIsProvided() {
+ Account acc = [SELECT Id FROM Account];
+ ContactPointAddress child = new ContactPointAddress(Name = 'RollupIntegrationRefresh', PreferenceRank = 99, ParentId = acc.Id);
+ insert child;
+
+ Flow.Interview flowInterview = new Flow.Interview.Rollup_Integration_Refresh_With_Empty_Collections(
+ new Map{ 'parentRecordIdForEmptyChildrenCollections' => acc.Id }
+ );
+
+ Test.startTest();
+ flowInterview.start();
+ Test.stopTest();
+
+ acc = [SELECT AnnualRevenue FROM Account WHERE Id = :acc.Id];
+ System.assertEquals(child.PreferenceRank, acc.AnnualRevenue);
+ }
}
diff --git a/extra-tests/classes/RollupCurrencyInfoTests.cls b/extra-tests/classes/RollupCurrencyInfoTests.cls
index 383e5b5a..d6256a94 100644
--- a/extra-tests/classes/RollupCurrencyInfoTests.cls
+++ b/extra-tests/classes/RollupCurrencyInfoTests.cls
@@ -83,6 +83,14 @@ private class RollupCurrencyInfoTests {
RollupCurrencyInfo.transform(new List{ camp }, Campaign.ActualCost, mockEurInfo.IsoCode, new List());
updatedCamp = (Campaign) RollupCurrencyInfo.getCalcItem(camp, mockEurInfo.IsoCode);
System.assertEquals((mockEurInfo.ConversionRate / (mockUsdInfo.ConversionRate / camp.ActualCost)).doubleValue(), updatedCamp.ActualCost);
+ // sanity check that updates on previously transformed fields still calculate correctly
+ camp.BudgetedCost = 6;
+ RollupCurrencyInfo.transform(new List{ camp }, Campaign.BudgetedCost, mockEurInfo.IsoCode, new List());
+ Campaign again = (Campaign) RollupCurrencyInfo.getCalcItem(camp, mockEurInfo.IsoCode);
+ System.assertEquals(
+ (mockEurInfo.ConversionRate / (mockUsdInfo.ConversionRate / camp.BudgetedCost)).setScale(mockEurInfo.DecimalPlaces),
+ again.BudgetedCost
+ );
}
@IsTest
diff --git a/extra-tests/classes/RollupEvaluatorTests.cls b/extra-tests/classes/RollupEvaluatorTests.cls
index 21471857..18516c0c 100644
--- a/extra-tests/classes/RollupEvaluatorTests.cls
+++ b/extra-tests/classes/RollupEvaluatorTests.cls
@@ -1258,6 +1258,15 @@ private class RollupEvaluatorTests {
System.assertEquals(false, eval.matches(nonMatch3));
}
+ @IsTest
+ static void stripsExtraParantheticalStructures() {
+ String whereClause = '(Name IN (\'0-Current\', \'1-30 Days\', \'31-60 Days\')) AND Name IN (\'0-Current\', \'1-30 Days\', \'31-60 Days\')';
+
+ RollupEvaluator eval = new RollupEvaluator.WhereFieldEvaluator(whereClause, ContactPointConsent.SObjectType);
+
+ System.assertEquals(true, eval.matches(new ContactPointConsent(Name = '0-Current')));
+ }
+
private static String getSoqlCompliantDatetime(Datetime dt) {
return dt.format('yyyy-MM-dd\'T\'HH:mm:ssZ');
}
diff --git a/extra-tests/classes/RollupLimitsTest.cls b/extra-tests/classes/RollupLimitsTest.cls
index bd2cfcb3..dc15d598 100644
--- a/extra-tests/classes/RollupLimitsTest.cls
+++ b/extra-tests/classes/RollupLimitsTest.cls
@@ -2,6 +2,15 @@
private class RollupLimitsTest {
@IsTest
static void correctlyReferencesOrgLimits() {
- System.assertEquals(false, new RollupLimits.Tester(RollupControl__mdt.getInstance('Org_Default'), false).hasExceededOrgAsyncLimit());
+ Assert.areEqual(false, new RollupLimits.Tester(RollupControl__mdt.getInstance('Org_Default'), false).hasExceededOrgAsyncLimit());
+ }
+
+ @IsTest
+ static void doesNotThrowForNullControl() {
+ RollupLimits.stubbedQueryRows = 50001;
+
+ Boolean hasExceededLimits = Rollup.hasExceededCurrentRollupLimits(null);
+
+ Assert.isTrue(hasExceededLimits);
}
}
diff --git a/extra-tests/classes/RollupRepositoryTests.cls b/extra-tests/classes/RollupRepositoryTests.cls
index 5da9796d..56ae8c84 100644
--- a/extra-tests/classes/RollupRepositoryTests.cls
+++ b/extra-tests/classes/RollupRepositoryTests.cls
@@ -23,6 +23,40 @@ private class RollupRepositoryTests {
Assert.isNull(ex);
}
+ @IsTest
+ static void orderBysEndUpCorrectlySorter() {
+ List orderBys = new List{
+ new RollupOrderBy__mdt(Ranking__c = 3, DeveloperName = 'd'),
+ new RollupOrderBy__mdt(Ranking__c = 0, DeveloperName = 'a'),
+ new RollupOrderBy__mdt(Ranking__c = 1, DeveloperName = 'b'),
+ new RollupOrderBy__mdt(Ranking__c = 2, DeveloperName = 'c')
+ };
+
+ orderBys.sort(new RollupRepository.OrderBySorter());
+
+ Assert.areEqual(0, orderBys.get(0).Ranking__c);
+ Assert.areEqual(1, orderBys.get(1).Ranking__c);
+ Assert.areEqual(2, orderBys.get(2).Ranking__c);
+ Assert.areEqual(3, orderBys.get(3).Ranking__c);
+ }
+
+ @IsTest
+ static void orderBysUseDeveloperNameForTieBreaker() {
+ List orderBys = new List{
+ new RollupOrderBy__mdt(Ranking__c = 0, DeveloperName = 'd'),
+ new RollupOrderBy__mdt(Ranking__c = 0, DeveloperName = 'a'),
+ new RollupOrderBy__mdt(Ranking__c = 0, DeveloperName = 'b'),
+ new RollupOrderBy__mdt(Ranking__c = 0, DeveloperName = 'c')
+ };
+
+ orderBys.sort(new RollupRepository.OrderBySorter());
+
+ Assert.areEqual('a', orderBys.get(0).DeveloperName);
+ Assert.areEqual('b', orderBys.get(1).DeveloperName);
+ Assert.areEqual('c', orderBys.get(2).DeveloperName);
+ Assert.areEqual('d', orderBys.get(3).DeveloperName);
+ }
+
/**
* Serialization proves that we don't get: `System.JSONException: Type unsupported in JSON: common.apex.methods.AccessLevelEnum`
*/
diff --git a/extra-tests/classes/RollupSObjectUpdaterTests.cls b/extra-tests/classes/RollupSObjectUpdaterTests.cls
index 89cc011b..a4b5dcec 100644
--- a/extra-tests/classes/RollupSObjectUpdaterTests.cls
+++ b/extra-tests/classes/RollupSObjectUpdaterTests.cls
@@ -157,6 +157,25 @@ public class RollupSObjectUpdaterTests {
Assert.areEqual('Update failed. First exception on row 0; first error: MISSING_ARGUMENT, Id not specified in an update call: []', message);
}
+ @IsTest
+ static void nullRollupControlDoesNotPreventExecution() {
+ RollupLimits.stubbedQueryRows = 50001;
+ RollupControl__mdt control = new RollupControl__mdt();
+ RollupSObjectUpdater updater = new RollupSObjectUpdater();
+
+ updater.forceSyncUpdate();
+ updater.addRollupControl(control);
+ control = null;
+
+ Exception ex;
+ try {
+ updater.doUpdate(new List{ new Account(Id = RollupTestUtils.createId(Account.SObjectType)) });
+ } catch (Exception e) {
+ ex = e;
+ }
+ Assert.isNull(ex);
+ }
+
public class MockUpdater implements RollupSObjectUpdater.IUpdater {
public void performUpdate(List recordsToUpdate, Database.DMLOptions options) {
mockUpdatedRecords = recordsToUpdate;
diff --git a/extra-tests/classes/RollupTestUtils.cls b/extra-tests/classes/RollupTestUtils.cls
index 371ba1d1..d5e72649 100644
--- a/extra-tests/classes/RollupTestUtils.cls
+++ b/extra-tests/classes/RollupTestUtils.cls
@@ -35,7 +35,7 @@ public class RollupTestUtils {
}
public static DMLMock loadAccountIdMock(List records) {
- Account acc = [SELECT Id FROM Account];
+ Account acc = [SELECT Id FROM Account LIMIT 1];
for (SObject record : records) {
record.put('ParentId', acc.Id);
}
diff --git a/extra-tests/classes/RollupTests.cls b/extra-tests/classes/RollupTests.cls
index cfc1c918..4769f840 100644
--- a/extra-tests/classes/RollupTests.cls
+++ b/extra-tests/classes/RollupTests.cls
@@ -1362,6 +1362,7 @@ private class RollupTests {
RollupTestUtils.DMLMock mock = RollupTestUtils.loadMock(new List{ somethingElseName });
Rollup.oldRecordsMap = new Map{ somethingElseName.Id => somethingElseName };
Rollup.apexContext = TriggerOperation.BEFORE_DELETE;
+ Rollup.onlyUseMockMetadata = true;
Rollup.rollupMetadata = new List{
new Rollup__mdt(
CalcItem__c = 'ContactPointAddress',
diff --git a/extra-tests/customMetadata/Rollup.RollupIntegrationParentRecordId.md-meta.xml b/extra-tests/customMetadata/Rollup.RollupIntegrationParentRecordId.md-meta.xml
new file mode 100644
index 00000000..3feb6af2
--- /dev/null
+++ b/extra-tests/customMetadata/Rollup.RollupIntegrationParentRecordId.md-meta.xml
@@ -0,0 +1,161 @@
+
+
+
+ false
+
+ CalcItemText__c
+ ContactPointAddress
+
+
+ CalcItemWhereClause__c
+ Name = 'RollupIntegrationRefresh'
+
+
+ CalcItem__c
+
+
+
+ ChangedFieldsOnCalcItem__c
+
+
+
+ ConcatDelimiter__c
+
+
+
+ CurrencyFieldMapping__c
+
+
+
+ Description__c
+
+
+
+ FullRecalculationDefaultNumberValue__c
+
+
+
+ FullRecalculationDefaultStringValue__c
+
+
+
+ GrandparentRelationshipFieldPath__c
+
+
+
+ GroupByFields__c
+
+
+
+ GroupByRowEndDelimiter__c
+
+
+
+ GroupByRowStartDelimiter__c
+
+
+
+ IsDistinct__c
+ false
+
+
+ IsFullRecordSet__c
+ false
+
+
+ IsRollupStartedFromParent__c
+ false
+
+
+ IsTableFormatted__c
+ false
+
+
+ LimitAmount__c
+
+
+
+ LookupFieldOnCalcItemText__c
+ ParentId
+
+
+ LookupFieldOnCalcItem__c
+
+
+
+ LookupFieldOnLookupObjectText__c
+ Id
+
+
+ LookupFieldOnLookupObject__c
+
+
+
+ LookupObjectText__c
+ Account
+
+
+ LookupObject__c
+
+
+
+ OneToManyGrandparentFields__c
+
+
+
+ OrderByFirstLast__c
+
+
+
+ RollupControl__c
+ Org_Defaults
+
+
+ RollupFieldOnCalcItemText__c
+ PreferenceRank
+
+
+ RollupFieldOnCalcItem__c
+
+
+
+ RollupFieldOnLookupObjectText__c
+ AnnualRevenue
+
+
+ RollupFieldOnLookupObject__c
+
+
+
+ RollupGrouping__c
+
+
+
+ RollupOperation__c
+ SUM
+
+
+ RollupToUltimateParent__c
+ false
+
+
+ SharingMode__c
+
+
+
+ ShouldRunWithoutCustomSettingEnabled__c
+ false
+
+
+ SplitConcatDelimiterOnCalcItem__c
+ false
+
+
+ UltimateParentLookup__c
+
+
+
diff --git a/extra-tests/flows/Rollup_Integration_Refresh_With_Empty_Collections.flow-meta.xml b/extra-tests/flows/Rollup_Integration_Refresh_With_Empty_Collections.flow-meta.xml
new file mode 100644
index 00000000..dbdffe8c
--- /dev/null
+++ b/extra-tests/flows/Rollup_Integration_Refresh_With_Empty_Collections.flow-meta.xml
@@ -0,0 +1,85 @@
+
+
+
+ Rollup_ContactPointConsent_Record
+
+ 176
+ 134
+ RollupFlowBulkProcessor
+ apex
+
+ T__oldRecordsToRollup
+ ContactPointAddress
+
+
+ T__recordsToRollup
+ ContactPointAddress
+
+ CurrentTransaction
+
+ calcItemTypeWhenRollupStartedFromParent
+
+ ContactPointAddress
+
+
+
+ deferProcessing
+
+ false
+
+
+
+ parentRecordIdForEmptyChildrenCollections
+
+ parentRecordIdForEmptyChildrenCollections
+
+
+
+ rollupContext
+
+ REFRESH
+
+
+ RollupFlowBulkProcessor
+ true
+ 1
+
+ 61.0
+ Default
+ Rollup Integration - Refresh With Empty Collections {!$Flow.CurrentDateTime}
+
+
+ BuilderType
+
+ LightningFlowBuilder
+
+
+
+ CanvasMode
+
+ AUTO_LAYOUT_CANVAS
+
+
+
+ OriginBuilderType
+
+ LightningFlowBuilder
+
+
+ AutoLaunchedFlow
+
+ 50
+ 0
+
+ Rollup_ContactPointConsent_Record
+
+
+ Obsolete
+
+ parentRecordIdForEmptyChildrenCollections
+ String
+ false
+ true
+ false
+
+
diff --git a/package.json b/package.json
index 1d9486c6..b22ffbca 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "apex-rollup",
- "version": "1.6.33",
+ "version": "1.6.34",
"description": "Fast, configurable, elastically scaling custom rollup solution. Apex Invocable action, one-liner Apex trigger/CMDT-driven logic, and scheduled Apex-ready.",
"repository": {
"type": "git",
diff --git a/plugins/ExtraCodeCoverage/README.md b/plugins/ExtraCodeCoverage/README.md
index 64b06f5c..12667eb2 100644
--- a/plugins/ExtraCodeCoverage/README.md
+++ b/plugins/ExtraCodeCoverage/README.md
@@ -1,11 +1,11 @@
# Extra Code Coverage
-
+
-
+
diff --git a/rollup-namespaced/README.md b/rollup-namespaced/README.md
index b9a62b9d..c3e0468a 100644
--- a/rollup-namespaced/README.md
+++ b/rollup-namespaced/README.md
@@ -18,12 +18,12 @@ For more info, see the base `README`.
## Deployment & Setup
-
+
-
+
diff --git a/rollup-namespaced/sfdx-project.json b/rollup-namespaced/sfdx-project.json
index eb59f208..227100ef 100644
--- a/rollup-namespaced/sfdx-project.json
+++ b/rollup-namespaced/sfdx-project.json
@@ -4,8 +4,8 @@
"default": true,
"package": "apex-rollup-namespaced",
"path": "rollup-namespaced/source/rollup",
- "versionName": "THIS_MONTH date literal bugfix, grandparent rollup updates",
- "versionNumber": "1.1.26.0",
+ "versionName": "Fixes parentRecordIdForEmptyChildrenCollections flow case",
+ "versionNumber": "1.1.27.0",
"versionDescription": "Fast, configurable, elastically scaling custom rollup solution. Apex Invocable action, one-liner Apex trigger/CMDT-driven logic, and scheduled Apex-ready.",
"releaseNotesUrl": "https://github.com/jamessimone/apex-rollup/releases/latest",
"unpackagedMetadata": {
@@ -29,6 +29,7 @@
"apex-rollup-namespaced@1.1.23": "04t6g000008ObNSAA0",
"apex-rollup-namespaced@1.1.24": "04t6g000008ObbqAAC",
"apex-rollup-namespaced@1.1.25": "04t6g000008Obc0AAC",
- "apex-rollup-namespaced@1.1.26": "04t6g000008ObeVAAS"
+ "apex-rollup-namespaced@1.1.26": "04t6g000008ObeVAAS",
+ "apex-rollup-namespaced@1.1.27": "04t6g000008OfJkAAK"
}
}
diff --git a/rollup/app/lwc/jsconfig.json b/rollup/app/lwc/jsconfig.json
index fd2127c2..5328c365 100644
--- a/rollup/app/lwc/jsconfig.json
+++ b/rollup/app/lwc/jsconfig.json
@@ -1,7 +1,10 @@
{
"compilerOptions": {
"experimentalDecorators": true,
- "baseUrl": "."
+ "baseUrl": ".",
+ "paths": {
+ "c/*": ["*"]
+ }
},
"include": ["**/*", "../../.sfdx/typings/lwc/**/*.d.ts", "../../../.sfdx/typings/lwc/**/*.d.ts"],
"typeAcquisition": {
diff --git a/rollup/core/classes/Rollup.cls b/rollup/core/classes/Rollup.cls
index 4cbfd779..6efa2a06 100644
--- a/rollup/core/classes/Rollup.cls
+++ b/rollup/core/classes/Rollup.cls
@@ -740,7 +740,7 @@ global without sharing virtual class Rollup implements RollupLogger.ToStringObje
public RollupGrouping__mdt rollupGrouping;
public Boolean shouldOverrideNoOp() {
- return this.parentRecordIdForEmptyChildrenCollections != null && calcItemTypeWhenRollupStartedFromParent != null;
+ return this.parentRecordIdForEmptyChildrenCollections != null && this.calcItemTypeWhenRollupStartedFromParent != null;
}
}
@@ -1648,10 +1648,10 @@ global without sharing virtual class Rollup implements RollupLogger.ToStringObje
// it would have been nice if this was an enum!
switch on header.changeType {
- when 'CREATE', 'GAP_CREATE', 'UNDELETE' {
+ when 'CREATE', 'UNDELETE' {
apexContext = TriggerOperation.AFTER_INSERT;
}
- when 'UPDATE', 'GAP_UPDATE' {
+ when 'UPDATE' {
apexContext = TriggerOperation.AFTER_UPDATE;
}
when 'DELETE' {
@@ -1850,7 +1850,7 @@ global without sharing virtual class Rollup implements RollupLogger.ToStringObje
RollupLimits.Tester limitTester = new RollupLimits.Tester(control, isContextAsync());
Boolean hasExceededLimits = limitTester.hasExceededLimits() && isDeferralAllowed;
if (limitTester.hasExceededLimits() && isDeferralAllowed) {
- Boolean isLoggingCurrentlyDisabled = control.IsRollupLoggingEnabled__c == false;
+ Boolean isLoggingCurrentlyDisabled = control?.IsRollupLoggingEnabled__c == false;
if (isLoggingCurrentlyDisabled) {
control.IsRollupLoggingEnabled__c = true;
RollupLogger.Instance.updateRollupControl(control);
@@ -1871,7 +1871,7 @@ global without sharing virtual class Rollup implements RollupLogger.ToStringObje
}
private static Rollup__mdt transformFlowInputToRollupMetadata(FlowInput flowInput, String rollupContext, SObjectType sObjectType) {
- return getFirstLastMetadata(
+ Rollup__mdt transformedMeta = getMetadataWithOrderBys(
new Rollup__mdt(
CalcItem__c = flowInput.isRollupStartedFromParent || String.isNotBlank(flowInput.calcItemTypeWhenRollupStartedFromParent)
? flowInput.calcItemTypeWhenRollupStartedFromParent
@@ -1908,6 +1908,8 @@ global without sharing virtual class Rollup implements RollupLogger.ToStringObje
),
flowInput.rollupOperation
);
+ transformedMeta.RollupControl__c = transformedMeta.RollupControl__r.Id;
+ return transformedMeta;
}
private static Boolean isContextAsync() {
@@ -2727,7 +2729,7 @@ global without sharing virtual class Rollup implements RollupLogger.ToStringObje
if (isFullRecalcOp(getBaseOperationName(rollupMetadata.RollupOperation__c)) || rollupMetadata.GroupByFields__c != null) {
rollupMetadata.IsFullRecordSet__c = true;
}
- rollupMetadata = getFirstLastMetadata(rollupMetadata, rollupOp.name());
+ rollupMetadata = getMetadataWithOrderBys(rollupMetadata, rollupOp.name());
RollupControl__mdt localControl;
if (rollupMetadata.RollupControl__c != null) {
@@ -2764,7 +2766,7 @@ global without sharing virtual class Rollup implements RollupLogger.ToStringObje
return rollupConductor;
}
- private static Rollup__mdt getFirstLastMetadata(Rollup__mdt meta, String rollupOpName) {
+ private static Rollup__mdt getMetadataWithOrderBys(Rollup__mdt meta, String rollupOpName) {
List replacementOrderBys;
if (meta.RollupOrderBys__r.isEmpty() && String.isNotBlank(meta.OrderByFirstLast__c)) {
List unparsedOrderBys = meta.OrderByFirstLast__c.split(',');
diff --git a/rollup/core/classes/RollupAsyncProcessor.cls b/rollup/core/classes/RollupAsyncProcessor.cls
index 04a0309c..f601183b 100644
--- a/rollup/core/classes/RollupAsyncProcessor.cls
+++ b/rollup/core/classes/RollupAsyncProcessor.cls
@@ -616,7 +616,7 @@ global virtual without sharing class RollupAsyncProcessor extends Rollup impleme
Boolean hasAlreadyBeenQueried = this.cachedQueryToAdditionalCalcItems.containsKey(query);
Boolean shouldLimitCalcItemQuery = false;
- Integer remainingQueryRowsLeft = 0;
+ Decimal remainingQueryRowsLeft = 0;
Integer outstandingItemCount = 0;
List additionalCalcItems = new List();
diff --git a/rollup/core/classes/RollupCurrencyInfo.cls b/rollup/core/classes/RollupCurrencyInfo.cls
index e21ffd7c..285696fb 100644
--- a/rollup/core/classes/RollupCurrencyInfo.cls
+++ b/rollup/core/classes/RollupCurrencyInfo.cls
@@ -234,12 +234,11 @@ public without sharing virtual class RollupCurrencyInfo {
}
private static String getHashKey(SObject calcItem, Schema.SObjectField opFieldOnCalcItem) {
- return '' + calcItem.Id + opFieldOnCalcItem.getDescribe().getName();
+ return '' + calcItem.Id + opFieldOnCalcItem.getDescribe().getName() + calcItem.hashCode();
}
private static Map getCurrencyMap() {
List currencyInfos = new List();
- Map currencyInfoMap = new Map();
if (mockCurrencyData != null) {
currencyInfos.addAll(mockCurrencyData);
} else {
@@ -248,6 +247,7 @@ public without sharing virtual class RollupCurrencyInfo {
currencyInfos.addAll((List) JSON.deserialize(JSON.serialize(REPOSITORY.setQuery(query).get()), List.class));
}
}
+ Map currencyInfoMap = new Map();
mapCurrencies(currencyInfoMap, currencyInfos);
return currencyInfoMap;
}
diff --git a/rollup/core/classes/RollupEvaluator.cls b/rollup/core/classes/RollupEvaluator.cls
index a0ea0e21..4fe6d602 100644
--- a/rollup/core/classes/RollupEvaluator.cls
+++ b/rollup/core/classes/RollupEvaluator.cls
@@ -393,7 +393,14 @@ public without sharing abstract class RollupEvaluator implements Rollup.Evaluato
String value = words[++index];
if (criteria.endsWithIgnoreCase('in')) {
while (value.endsWith(')') == false) {
- value += ' ' + words[++index];
+ String nextWord = words[++index];
+ value += ' ' + nextWord;
+ if (index + 1 == words.size()) {
+ break;
+ }
+ }
+ if (value.startsWith('(') && value.endsWith(')') == false) {
+ value += ')';
}
} else if (value.startsWith('\'') && value.endsWith('\'') == false) {
String tempVal = value;
diff --git a/rollup/core/classes/RollupFlowBulkProcessor.cls b/rollup/core/classes/RollupFlowBulkProcessor.cls
index 79ea0bbf..3341bcc3 100644
--- a/rollup/core/classes/RollupFlowBulkProcessor.cls
+++ b/rollup/core/classes/RollupFlowBulkProcessor.cls
@@ -77,27 +77,23 @@ global without sharing class RollupFlowBulkProcessor {
List validInputs = new List();
for (FlowInput flowInput : flowInputs) {
Rollup.FlowOutput output = new Rollup.FlowOutput();
- if (flowInput.recordsToRollup?.isEmpty() != false) {
+ if (flowInput.recordsToRollup?.isEmpty() != false && flowInput.parentRecordIdForEmptyChildrenCollections == null) {
output.message = 'No records';
outputs.add(output);
} else {
List rollupMetadata = Rollup.getMetadataFromCache(Rollup__mdt.SObjectType);
// for some reason, lists passed from Flow to Apex report their SObjectType as null. womp.
Schema.SObjectType sObjectType = flowInput.recordsToRollup?.get(0).getSObjectType();
+ String childName = sObjectType?.getDescribe(SObjectDescribeOptions.DEFERRED).getName();
for (Rollup__mdt meta : rollupMetadata) {
if (
meta.IsRollupStartedFromParent__c ||
sObjectType != null && String.isNotBlank(meta.GrandparentRelationshipFieldPath__c) && Rollup.getPartOfGrandparentChain(meta, sObjectType) != null
) {
- flowInput.calcItemTypeWhenRollupStartedFromParent = flowInput.calcItemTypeWhenRollupStartedFromParent != null
- ? flowInput.calcItemTypeWhenRollupStartedFromParent
- : meta.CalcItem__c;
+ flowInput.calcItemTypeWhenRollupStartedFromParent = flowInput.calcItemTypeWhenRollupStartedFromParent ?? meta.CalcItem__c;
}
- if (
- flowInput.recordsToRollup == null ||
- meta.CalcItem__c == sObjectType?.getDescribe(SObjectDescribeOptions.DEFERRED).getName() ||
- flowInput.calcItemTypeWhenRollupStartedFromParent == meta.CalcItem__c
- ) {
+ Boolean isMatchingParentSide = flowInput.calcItemTypeWhenRollupStartedFromParent == meta.CalcItem__c;
+ if ((flowInput.recordsToRollup?.isEmpty() != false && isMatchingParentSide) || meta.CalcItem__c == childName || isMatchingParentSide) {
Rollup.FlowInput input = new Rollup.FlowInput();
validInputs.add(input);
// pertinent fields from CMDT (can be overridden by optional flow properties)
diff --git a/rollup/core/classes/RollupLimits.cls b/rollup/core/classes/RollupLimits.cls
index d833b2bc..2c6832e7 100644
--- a/rollup/core/classes/RollupLimits.cls
+++ b/rollup/core/classes/RollupLimits.cls
@@ -7,7 +7,6 @@ public without sharing class RollupLimits {
private static Integer stubAsyncTimeoutInterval;
private static final Integer SYNC_TIMEOUT_INTERVAL_MS = 1500;
- private static final Integer LIMIT_HEAP_SIZE = Limits.getLimitHeapSize();
private static final Integer LIMIT_QUERY_ROWS = 50000;
private static final Integer ASYNC_TIMEOUT_INTERVAL_MS {
@@ -39,7 +38,6 @@ public without sharing class RollupLimits {
public final Boolean hasExceededQueryNumberLimit;
public final Boolean hasExceededQueryRowLimit;
- public final Boolean hasExceededHeapSizeLimit;
public final Boolean hasExceededDMLRowLimit;
public final Boolean hasExceededCPUTimeLimit;
@@ -48,7 +46,6 @@ public without sharing class RollupLimits {
this.isRunningAsync = isRunningAsync;
this.hasExceededQueryNumberLimit = this.control?.MaxNumberOfQueries__c < Limits.getQueries();
this.hasExceededQueryRowLimit = this.getRemainingQueryRows() < 0;
- this.hasExceededHeapSizeLimit = (LIMIT_HEAP_SIZE - 2000000) < Limits.getHeapSize();
this.hasExceededDMLRowLimit = this.control?.MaxParentRowsUpdatedAtOnce__c < Limits.getDmlRows();
Integer intervalTillTimeout = this.isRunningAsync ? ASYNC_TIMEOUT_INTERVAL_MS : SYNC_TIMEOUT_INTERVAL_MS;
@@ -56,7 +53,6 @@ public without sharing class RollupLimits {
this.hasExceededOverallLimits =
this.hasExceededQueryNumberLimit ||
this.hasExceededQueryRowLimit ||
- this.hasExceededHeapSizeLimit ||
this.hasExceededDMLRowLimit ||
this.hasExceededCPUTimeLimit;
}
@@ -65,10 +61,10 @@ public without sharing class RollupLimits {
return this.hasExceededOverallLimits;
}
- public Integer getRemainingQueryRows() {
+ public Decimal getRemainingQueryRows() {
Integer queryRowsUsed = stubbedQueryRows ?? Limits.getQueryRows();
- Integer remainingQueryRows = (this.control.MaxQueryRows__c?.intValue() ?? LIMIT_QUERY_ROWS) - queryRowsUsed;
- return remainingQueryRows > 0 ? remainingQueryRows : 0;
+ Decimal maxQueryRows = this.control.MaxQueryRows__c != null && this.control.MaxQueryRows__c <= LIMIT_QUERY_ROWS ? this.control.MaxQueryRows__c : LIMIT_QUERY_ROWS;
+ return maxQueryRows - queryRowsUsed;
}
public Boolean hasExceededOrgAsyncLimit() {
diff --git a/rollup/core/classes/RollupLogger.cls b/rollup/core/classes/RollupLogger.cls
index 7c054c18..d5e43bdd 100644
--- a/rollup/core/classes/RollupLogger.cls
+++ b/rollup/core/classes/RollupLogger.cls
@@ -1,7 +1,7 @@
global without sharing virtual class RollupLogger implements ILogger {
@TestVisible
// this gets updated via the pipeline as the version number gets incremented
- private static final String CURRENT_VERSION_NUMBER = 'v1.6.33';
+ private static final String CURRENT_VERSION_NUMBER = 'v1.6.34';
private static final System.LoggingLevel FALLBACK_LOGGING_LEVEL = System.LoggingLevel.DEBUG;
private static final RollupPlugin PLUGIN = new RollupPlugin();
diff --git a/rollup/core/classes/RollupRepository.cls b/rollup/core/classes/RollupRepository.cls
index aeec4f56..471dbe9a 100644
--- a/rollup/core/classes/RollupRepository.cls
+++ b/rollup/core/classes/RollupRepository.cls
@@ -82,59 +82,59 @@ public without sharing class RollupRepository implements RollupLogger.ToStringOb
@SuppressWarnings('PMD.ApexCRUDViolation')
public static List getRollupMetadata() {
List matchingMetadata = [
- SELECT
- MasterLabel,
- DeveloperName,
- LookupObject__c,
- LookupObjectText__c,
- LookupObject__r.QualifiedApiName,
- CalcItem__c,
- CalcItemText__c,
- CalcItem__r.QualifiedApiName,
- RollupFieldOnCalcItem__c,
- RollupFieldOnCalcItemText__c,
- RollupFieldOnCalcItem__r.QualifiedApiName,
- LookupFieldOnCalcItem__c,
- LookupFieldOnCalcItemText__c,
- LookupFieldOnCalcItem__r.QualifiedApiName,
- LookupFieldOnLookupObject__c,
- LookupFieldOnLookupObjectText__c,
- LookupFieldOnLookupObject__r.QualifiedApiName,
- RollupFieldOnLookupObject__c,
- RollupFieldOnLookupObjectText__c,
- RollupFieldOnLookupObject__r.QualifiedApiName,
- UltimateParentLookup__c,
- UltimateParentLookup__r.QualifiedApiName,
- CalcItemWhereClause__c,
- ChangedFieldsOnCalcItem__c,
- ConcatDelimiter__c,
- CurrencyFieldMapping__c,
- FullRecalculationDefaultNumberValue__c,
- FullRecalculationDefaultStringValue__c,
- GrandparentRelationshipFieldPath__c,
- GroupByFields__c,
- GroupByRowEndDelimiter__c,
- GroupByRowStartDelimiter__c,
- IsDistinct__c,
- IsFullRecordSet__c,
- IsRollupStartedFromParent__c,
- IsTableFormatted__c,
- LimitAmount__c,
- OneToManyGrandparentFields__c,
- OrderByFirstLast__c,
- RollupControl__c,
- RollupOperation__c,
- RollupToUltimateParent__c,
- SharingMode__c,
- ShouldRunWithoutCustomSettingEnabled__c,
- SplitConcatDelimiterOnCalcItem__c,
- (SELECT Id, FieldName__c, NullSortOrder__c, Ranking__c, SortOrder__c FROM RollupOrderBys__r ORDER BY Ranking__c, DeveloperName),
- RollupGrouping__r.Id,
- RollupGrouping__r.RollupOperation__c
- FROM Rollup__mdt
- WHERE RollupControl__r.ShouldAbortRun__c = FALSE
- ORDER BY LookupObject__c
- ];
+ SELECT
+ MasterLabel,
+ DeveloperName,
+ LookupObject__c,
+ LookupObjectText__c,
+ LookupObject__r.QualifiedApiName,
+ CalcItem__c,
+ CalcItemText__c,
+ CalcItem__r.QualifiedApiName,
+ RollupFieldOnCalcItem__c,
+ RollupFieldOnCalcItemText__c,
+ RollupFieldOnCalcItem__r.QualifiedApiName,
+ LookupFieldOnCalcItem__c,
+ LookupFieldOnCalcItemText__c,
+ LookupFieldOnCalcItem__r.QualifiedApiName,
+ LookupFieldOnLookupObject__c,
+ LookupFieldOnLookupObjectText__c,
+ LookupFieldOnLookupObject__r.QualifiedApiName,
+ RollupFieldOnLookupObject__c,
+ RollupFieldOnLookupObjectText__c,
+ RollupFieldOnLookupObject__r.QualifiedApiName,
+ UltimateParentLookup__c,
+ UltimateParentLookup__r.QualifiedApiName,
+ CalcItemWhereClause__c,
+ ChangedFieldsOnCalcItem__c,
+ ConcatDelimiter__c,
+ CurrencyFieldMapping__c,
+ FullRecalculationDefaultNumberValue__c,
+ FullRecalculationDefaultStringValue__c,
+ GrandparentRelationshipFieldPath__c,
+ GroupByFields__c,
+ GroupByRowEndDelimiter__c,
+ GroupByRowStartDelimiter__c,
+ IsDistinct__c,
+ IsFullRecordSet__c,
+ IsRollupStartedFromParent__c,
+ IsTableFormatted__c,
+ LimitAmount__c,
+ OneToManyGrandparentFields__c,
+ OrderByFirstLast__c,
+ RollupControl__c,
+ RollupOperation__c,
+ RollupToUltimateParent__c,
+ SharingMode__c,
+ ShouldRunWithoutCustomSettingEnabled__c,
+ SplitConcatDelimiterOnCalcItem__c,
+ (SELECT Id, FieldName__c, NullSortOrder__c, Ranking__c, SortOrder__c FROM RollupOrderBys__r),
+ RollupGrouping__r.Id,
+ RollupGrouping__r.RollupOperation__c
+ FROM Rollup__mdt
+ WHERE RollupControl__r.ShouldAbortRun__c = FALSE
+ ORDER BY LookupObject__c
+ ];
// we have to do transforms on the Entity Definition fields because custom objects/custom fields
// have references that otherwise won't work with the rest of the code
for (Rollup__mdt meta : matchingMetadata) {
@@ -149,6 +149,7 @@ public without sharing class RollupRepository implements RollupLogger.ToStringOb
meta.GroupByRowStartDelimiter__c = meta.GroupByRowStartDelimiter__c?.unescapeJava();
meta.SharingMode__c = meta.SharingMode__c ?? RollupMetaPicklists.SharingMode.SystemLevel;
meta.UltimateParentLookup__c = meta.UltimateParentLookup__r.QualifiedApiName;
+ meta.RollupOrderBys__r.sort(new OrderBySorter());
}
return matchingMetadata;
@@ -161,4 +162,34 @@ public without sharing class RollupRepository implements RollupLogger.ToStringOb
private System.AccessLevel transformPermissionLevel(RunAsMode currentRunAs) {
return currentRunAs == RunAsMode.USER ? System.AccessLevel.USER_MODE : System.AccessLevel.SYSTEM_MODE;
}
+
+ public class OrderBySorter implements System.Comparator {
+ private final List sortFields = new List{ RollupOrderBy__mdt.Ranking__c, RollupOrderBy__mdt.DeveloperName };
+ public Integer compare(RollupOrderBy__mdt first, RollupOrderBy__mdt second) {
+ Integer returnValue = 0;
+ List localSortFields = new List(this.sortFields);
+ while (returnValue == 0 && localSortFields.isEmpty() == false) {
+ Schema.SObjectField field = localSortFields.remove(0);
+ Object firstSortValue = first.get(field);
+ Object secondSortValue = second.get(field);
+
+ if (firstSortValue instanceof Decimal) {
+ returnValue = this.getDecimalSortedValue((Decimal) firstSortValue, (Decimal) secondSortValue);
+ } else if (firstSortValue instanceof String) {
+ returnValue = ((String) firstSortValue).compareTo((String) secondSortValue);
+ }
+ }
+ return returnValue;
+ }
+
+ private Integer getDecimalSortedValue(Decimal first, Decimal second) {
+ Integer returnValue = 0;
+ if (first > second) {
+ returnValue = 1;
+ } else if (first < second) {
+ returnValue = -1;
+ }
+ return returnValue;
+ }
+ }
}
diff --git a/rollup/core/objects/Rollup__mdt/validationRules/Rollup_Field_Parent_Required.validationRule-meta.xml b/rollup/core/objects/Rollup__mdt/validationRules/Rollup_Field_Parent_Required.validationRule-meta.xml
index ab1dc811..0669e3fb 100644
--- a/rollup/core/objects/Rollup__mdt/validationRules/Rollup_Field_Parent_Required.validationRule-meta.xml
+++ b/rollup/core/objects/Rollup__mdt/validationRules/Rollup_Field_Parent_Required.validationRule-meta.xml
@@ -1,4 +1,4 @@
-
+
Rollup_Field_Parent_Required
true
diff --git a/sfdx-project.json b/sfdx-project.json
index 1acb6d31..2ae33484 100644
--- a/sfdx-project.json
+++ b/sfdx-project.json
@@ -5,8 +5,8 @@
"package": "apex-rollup",
"path": "rollup",
"scopeProfiles": true,
- "versionName": "THIS_MONTH date literal bugfix, grandparent rollup updates",
- "versionNumber": "1.6.33.0",
+ "versionName": "Fixes parentRecordIdForEmptyChildrenCollections flow case",
+ "versionNumber": "1.6.34.0",
"versionDescription": "Fast, configurable, elastically scaling custom rollup solution. Apex Invocable action, one-liner Apex trigger/CMDT-driven logic, and scheduled Apex-ready.",
"releaseNotesUrl": "https://github.com/jamessimone/apex-rollup/releases/latest",
"unpackagedMetadata": {
@@ -69,12 +69,12 @@
"package": "Apex Rollup - Extra Code Coverage",
"versionDescription": "This plugin adds code coverage for Apex Rollup if you need additional code coverage for the base package",
"versionName": "Updating code coverage",
- "versionNumber": "0.0.23.0",
+ "versionNumber": "0.0.24.0",
"default": false,
"scopeProfiles": true,
"dependencies": [
{
- "package": "apex-rollup@1.6.30"
+ "package": "apex-rollup@1.6.34"
}
]
},
@@ -91,21 +91,22 @@
"Apex Rollup - Custom Logger@0.0.11-0": "04t6g000008SirvAAC",
"Apex Rollup - Custom Logger@0.0.12-0": "04t6g000008SjqIAAS",
"Apex Rollup - Extra Code Coverage": "0Ho6g000000GnCWCA0",
- "Apex Rollup - Extra Code Coverage@0.0.22": "04t6g000008ObCiAAK",
"Apex Rollup - Extra Code Coverage@0.0.23": "04t6g000008ObVmAAK",
+ "Apex Rollup - Extra Code Coverage@0.0.24": "04t6g000008ObgCAAS",
"Apex Rollup - Nebula Logger": "0Ho6g000000Gn8PCAS",
"Apex Rollup - Nebula Logger@0.0.7-0": "04t6g000008b0O7AAI",
"Apex Rollup - Nebula Logger@0.0.8-0": "04t6g000007zM6tAAE",
"Apex Rollup - Rollup Callback": "0Ho6g000000GnA1CAK",
"Apex Rollup - Rollup Callback@0.0.2-0": "04t6g000008ShztAAC",
"Apex Rollup - Rollup Callback@0.0.3-0": "04t6g000008Sis0AAC",
- "Nebula Logger - Core@4.8.0-NEXT-ignore-origin-method": "04t5Y0000015lslQAA",
+ "Nebula Logger - Core@4.14.4-optionally-auto-call-lightning-logger-lwc": "04t5Y0000015oRNQAY",
"apex-rollup": "0Ho6g000000TNcOCAW",
"apex-rollup@1.6.28": "04t6g000008ObN8AAK",
"apex-rollup@1.6.29": "04t6g000008ObNNAA0",
"apex-rollup@1.6.30": "04t6g000008ObVhAAK",
"apex-rollup@1.6.31": "04t6g000008ObblAAC",
"apex-rollup@1.6.32": "04t6g000008ObbvAAC",
- "apex-rollup@1.6.33": "04t6g000008ObeQAAS"
+ "apex-rollup@1.6.33": "04t6g000008ObeQAAS",
+ "apex-rollup@1.6.34": "04t6g000008OfJfAAK"
}
}