From 1acc7d5a1b3ea3e01adab4be4b61d1736afa7552 Mon Sep 17 00:00:00 2001 From: James Simone <16430727+jamessimone@users.noreply.github.com> Date: Wed, 2 Oct 2024 09:00:16 -0600 Subject: [PATCH] v1.6.35 - Rollup Order By Bugfix (#632) * Fixes an issue reported by Katherine West where rollup ordering wasn't being properly applied * Fixes #629 by properly handling text template HTML tags (when present) for the Full Recalc CMDT-driven Invocable rollup action --- README.md | 4 +- .../classes/RollupDateLiteralTests.cls | 9 ++- .../RollupFlowFullRecalcDispatcherTests.cls | 75 +++++++++++++++++++ ....Integration_NumberValue_FIRST.md-meta.xml | 29 +++++++ ..._Comma_Separated_Full_Recalc.flow-meta.xml | 64 ++++++++++++++++ package.json | 2 +- plugins/RollupCallback/README.md | 2 +- rollup-namespaced/README.md | 4 +- rollup-namespaced/sfdx-project.json | 7 +- rollup/core/classes/RollupCalculator.cls | 3 +- rollup/core/classes/RollupDateLiteral.cls | 18 ++++- .../RollupFlowFullRecalcDispatcher.cls | 2 +- rollup/core/classes/RollupLogger.cls | 2 +- rollup/core/classes/RollupRepository.cls | 3 +- sfdx-project.json | 10 +-- 15 files changed, 210 insertions(+), 24 deletions(-) create mode 100644 extra-tests/customMetadata/RollupOrderBy.Integration_NumberValue_FIRST.md-meta.xml create mode 100644 extra-tests/flows/Rollup_Integration_Comma_Separated_Full_Recalc.flow-meta.xml diff --git a/README.md b/README.md index 581ad92f..8ca02079 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 - + Deploy to Salesforce - + Deploy to Salesforce Sandbox diff --git a/extra-tests/classes/RollupDateLiteralTests.cls b/extra-tests/classes/RollupDateLiteralTests.cls index 1e7b374d..4ed69395 100644 --- a/extra-tests/classes/RollupDateLiteralTests.cls +++ b/extra-tests/classes/RollupDateLiteralTests.cls @@ -224,12 +224,15 @@ private class RollupDateLiteralTests { @IsTest static void shouldWorkForLastMonth() { runTestForLiteral('LAST_MONTH'); + User australiaUser = [SELECT Id FROM User WHERE LastName = 'Australia User']; + System.runAs(australiaUser) { + runTestForLiteral('LAST_MONTH'); + } } @IsTest static void shouldWorkForThisMonth() { runTestForLiteral('THIS_MONTH'); - // TODO extend this more generally throughout this test class User australiaUser = [SELECT Id FROM User WHERE LastName = 'Australia User']; System.runAs(australiaUser) { runTestForLiteral('THIS_MONTH'); @@ -239,6 +242,10 @@ private class RollupDateLiteralTests { @IsTest static void shouldWorkForNextMonth() { runTestForLiteral('NEXT_MONTH'); + User australiaUser = [SELECT Id FROM User WHERE LastName = 'Australia User']; + System.runAs(australiaUser) { + runTestForLiteral('NEXT_MONTH'); + } } @IsTest diff --git a/extra-tests/classes/RollupFlowFullRecalcDispatcherTests.cls b/extra-tests/classes/RollupFlowFullRecalcDispatcherTests.cls index a84bba15..5dea8360 100644 --- a/extra-tests/classes/RollupFlowFullRecalcDispatcherTests.cls +++ b/extra-tests/classes/RollupFlowFullRecalcDispatcherTests.cls @@ -120,6 +120,81 @@ private class RollupFlowFullRecalcDispatcherTests { System.assertEquals(5, user.Latitude); } + @IsTest + static void supportsTextTemplateInputVariables() { + Rollup.onlyUseMockMetadata = true; + Rollup.rollupMetadata = new List{ + new Rollup__mdt( + DeveloperName = 'cmdt1', + CalcItem__c = 'ContactPointAddress', + RollupFieldOnCalcItem__c = 'PreferenceRank', + LookupFieldOnCalcItem__c = 'ParentId', + LookupObject__c = 'Account', + LookupFieldOnLookupObject__c = 'Id', + RollupFieldOnLookupObject__c = 'AnnualRevenue', + RollupOperation__c = 'SUM', + CalcItemWhereClause__c = 'PreferenceRank = 1' + ), + new Rollup__mdt( + DeveloperName = 'cmdt2', + CalcItem__c = 'ContactPointAddress', + RollupFieldOnCalcItem__c = 'PreferenceRank', + LookupFieldOnCalcItem__c = 'ParentId', + LookupObject__c = 'Account', + LookupFieldOnLookupObject__c = 'Id', + RollupFieldOnLookupObject__c = 'NumberOfEmployees', + RollupOperation__c = 'COUNT' + ) + }; + List flowInputs = getFlowInputs(Rollup.rollupMetadata); + String exampleTextTemplate = '

{0}

'; + flowInputs[0].rollupDeveloperNames = String.format(exampleTextTemplate, new List{ flowInputs[0].rollupDeveloperNames }); + + Test.startTest(); + RollupFlowFullRecalcDispatcher.performFullRecalcRollups(flowInputs); + Test.stopTest(); + + Account acc = [SELECT AnnualRevenue, NumberOfEmployees FROM Account]; + System.assertEquals(6, acc.AnnualRevenue); + System.assertEquals(6, acc.NumberOfEmployees); + } + + @IsTest + static void integrationSupportsTextTemplateVariables() { + Rollup.onlyUseMockMetadata = true; + Rollup.rollupMetadata = new List{ + new Rollup__mdt( + DeveloperName = 'cmdt1', + CalcItem__c = 'ContactPointAddress', + RollupFieldOnCalcItem__c = 'PreferenceRank', + LookupFieldOnCalcItem__c = 'ParentId', + LookupObject__c = 'Account', + LookupFieldOnLookupObject__c = 'Id', + RollupFieldOnLookupObject__c = 'AnnualRevenue', + RollupOperation__c = 'SUM', + CalcItemWhereClause__c = 'PreferenceRank = 1' + ), + new Rollup__mdt( + DeveloperName = 'cmdt2', + CalcItem__c = 'ContactPointAddress', + RollupFieldOnCalcItem__c = 'PreferenceRank', + LookupFieldOnCalcItem__c = 'ParentId', + LookupObject__c = 'Account', + LookupFieldOnLookupObject__c = 'Id', + RollupFieldOnLookupObject__c = 'NumberOfEmployees', + RollupOperation__c = 'COUNT' + ) + }; + + Test.startTest(); + new Flow.Interview.Rollup_Integration_Comma_Separated_Full_Recalc(new Map{ 'rollupNames' => 'cmdt1, cmdt2' }).start(); + Test.stopTest(); + + Account acc = [SELECT AnnualRevenue, NumberOfEmployees FROM Account]; + System.assertEquals(6, acc.AnnualRevenue); + System.assertEquals(6, acc.NumberOfEmployees); + } + private static List getFlowInputs(List metas) { List flowInputs = new List(); RollupFlowFullRecalcDispatcher.FlowInput input = new RollupFlowFullRecalcDispatcher.FlowInput(); diff --git a/extra-tests/customMetadata/RollupOrderBy.Integration_NumberValue_FIRST.md-meta.xml b/extra-tests/customMetadata/RollupOrderBy.Integration_NumberValue_FIRST.md-meta.xml new file mode 100644 index 00000000..fa49e308 --- /dev/null +++ b/extra-tests/customMetadata/RollupOrderBy.Integration_NumberValue_FIRST.md-meta.xml @@ -0,0 +1,29 @@ + + + + false + + FieldName__c + NumberField__c + + + NullSortOrder__c + NULLS FIRST + + + Ranking__c + 1.0 + + + Rollup__c + RollupIntegrationChildRollupText + + + SortOrder__c + Ascending + + diff --git a/extra-tests/flows/Rollup_Integration_Comma_Separated_Full_Recalc.flow-meta.xml b/extra-tests/flows/Rollup_Integration_Comma_Separated_Full_Recalc.flow-meta.xml new file mode 100644 index 00000000..13a5e477 --- /dev/null +++ b/extra-tests/flows/Rollup_Integration_Comma_Separated_Full_Recalc.flow-meta.xml @@ -0,0 +1,64 @@ + + + + Recalc_Rollups + + 176 + 134 + RollupFlowFullRecalcDispatcher + apex + CurrentTransaction + + rollupDeveloperNames + + Rollups_Comma_Separated + + + RollupFlowFullRecalcDispatcher + true + 1 + + 61.0 + Default + Rollup Integration: Comma-Separated Full Recalc {!$Flow.CurrentDateTime} + + + BuilderType + + LightningFlowBuilder + + + + CanvasMode + + AUTO_LAYOUT_CANVAS + + + + OriginBuilderType + + LightningFlowBuilder + + + AutoLaunchedFlow + + 50 + 0 + + Recalc_Rollups + + + Draft + + Rollups_Comma_Separated + false + <p>{!rollupNames}</p> + + + rollupNames + String + false + true + false + + diff --git a/package.json b/package.json index 52b444a2..b56c89fa 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "apex-rollup", - "version": "1.6.34", + "version": "1.6.35", "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/RollupCallback/README.md b/plugins/RollupCallback/README.md index 7192ab89..cceb6fa8 100644 --- a/plugins/RollupCallback/README.md +++ b/plugins/RollupCallback/README.md @@ -109,7 +109,7 @@ public class SubflowRollupDispatcher implements RollupSObjectUpdater.IDispatcher decorator.FieldNames = new List(record.getPopulatedFieldsAsMap().keySet()); wrappedRecords.add(decorator); } - Flow.Interview rollupSubflow = Flow.Interview.RollupSubflow( + Flow.Interview rollupSubflow = new Flow.Interview.RollupSubflow( new Map{ 'records' => wrappedRecords } diff --git a/rollup-namespaced/README.md b/rollup-namespaced/README.md index c3e0468a..dcc70b51 100644 --- a/rollup-namespaced/README.md +++ b/rollup-namespaced/README.md @@ -18,12 +18,12 @@ For more info, see the base `README`. ## Deployment & Setup - + Deploy to Salesforce - + Deploy to Salesforce Sandbox diff --git a/rollup-namespaced/sfdx-project.json b/rollup-namespaced/sfdx-project.json index 227100ef..f84c61c0 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": "Fixes parentRecordIdForEmptyChildrenCollections flow case", - "versionNumber": "1.1.27.0", + "versionName": "Fixes Rollup Order By sorting to always be deterministic, allows text templates in Full Recalc CMDT-driven Invocable invocable", + "versionNumber": "1.1.28.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": { @@ -30,6 +30,7 @@ "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.27": "04t6g000008OfJkAAK" + "apex-rollup-namespaced@1.1.27": "04t6g000008OfJkAAK", + "apex-rollup-namespaced@1.1.28": "04t6g000008OfKnAAK" } } diff --git a/rollup/core/classes/RollupCalculator.cls b/rollup/core/classes/RollupCalculator.cls index b4be10b7..82e7e8e4 100644 --- a/rollup/core/classes/RollupCalculator.cls +++ b/rollup/core/classes/RollupCalculator.cls @@ -321,8 +321,9 @@ public without sharing abstract class RollupCalculator { protected List winnowItems(List items, Map oldCalcItems) { List orderBys = this.metadata?.LimitAmount__c != null && this.metadata.RollupOrderBys__r.isEmpty() ? new List{ new RollupOrderBy__mdt(FieldName__c = 'Id', Ranking__c = 0) } - : this.metadata.RollupOrderBys__r; + : new List(this.metadata.RollupOrderBys__r); if (orderBys.isEmpty() == false) { + orderBys.sort(new RollupRepository.OrderBySorter()); items.sort(new RollupCalcItemSorter(orderBys)); } List winnowedItems = new List(); diff --git a/rollup/core/classes/RollupDateLiteral.cls b/rollup/core/classes/RollupDateLiteral.cls index 046cc0c6..4bad05c4 100644 --- a/rollup/core/classes/RollupDateLiteral.cls +++ b/rollup/core/classes/RollupDateLiteral.cls @@ -354,7 +354,11 @@ public without sharing abstract class RollupDateLiteral { private class LastMonthLiteral extends RollupDateLiteral { public LastMonthLiteral() { this.ref = getRelativeDatetime(System.today().addMonths(-1).toStartOfMonth(), START_TIME); - this.bound = offsetToFirstDay(getRelativeDatetime(this.ref.addMonths(1).dateGmt(), END_TIME)); + Date relativeDate = this.ref.addMonths(1).date(); + if (relativeDate.day() == 1) { + relativeDate = relativeDate.addDays(-1); + } + this.bound = offsetToFirstDay(getRelativeDatetime(relativeDate, END_TIME)); } } @@ -364,7 +368,11 @@ public without sharing abstract class RollupDateLiteral { private class ThisMonthLiteral extends RollupDateLiteral { public ThisMonthLiteral() { this.ref = getRelativeDatetime(System.today().toStartOfMonth(), START_TIME); - this.bound = offsetToFirstDay(getRelativeDatetime(this.ref.addMonths(1).dateGmt(), END_TIME)); + Date relativeDate = this.ref.addMonths(1).date(); + if (relativeDate.day() == 1) { + relativeDate = relativeDate.addDays(-1); + } + this.bound = offsetToFirstDay(getRelativeDatetime(relativeDate, END_TIME)); } } @@ -374,7 +382,11 @@ public without sharing abstract class RollupDateLiteral { private class NextMonthLiteral extends RollupDateLiteral { public NextMonthLiteral() { this.ref = getRelativeDatetime(System.today().toStartOfMonth().addMonths(1), START_TIME); - this.bound = offsetToFirstDay(getRelativeDatetime(this.ref.addMonths(1).dateGmt(), END_TIME)); + Date relativeDate = this.ref.addMonths(1).date(); + if (relativeDate.day() == 1) { + relativeDate = relativeDate.addDays(-1); + } + this.bound = offsetToFirstDay(getRelativeDatetime(relativeDate, END_TIME)); } } diff --git a/rollup/core/classes/RollupFlowFullRecalcDispatcher.cls b/rollup/core/classes/RollupFlowFullRecalcDispatcher.cls index a59326bf..0fb35935 100644 --- a/rollup/core/classes/RollupFlowFullRecalcDispatcher.cls +++ b/rollup/core/classes/RollupFlowFullRecalcDispatcher.cls @@ -15,7 +15,7 @@ global without sharing class RollupFlowFullRecalcDispatcher { if (String.isBlank(input.rollupDeveloperNames)) { throw new IllegalArgumentException('Comma-separated list of Rollup__mdt DeveloperName(s) was not provided'); } - List splitListOfApiNames = input.rollupDeveloperNames.split(','); + List splitListOfApiNames = input.rollupDeveloperNames.stripHtmlTags().split(','); for (String apiName : splitListOfApiNames) { rollupDeveloperNames.add(apiName.trim()); } diff --git a/rollup/core/classes/RollupLogger.cls b/rollup/core/classes/RollupLogger.cls index d5e43bdd..4423c921 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.34'; + private static final String CURRENT_VERSION_NUMBER = 'v1.6.35'; 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 471dbe9a..18147540 100644 --- a/rollup/core/classes/RollupRepository.cls +++ b/rollup/core/classes/RollupRepository.cls @@ -128,7 +128,7 @@ public without sharing class RollupRepository implements RollupLogger.ToStringOb SharingMode__c, ShouldRunWithoutCustomSettingEnabled__c, SplitConcatDelimiterOnCalcItem__c, - (SELECT Id, FieldName__c, NullSortOrder__c, Ranking__c, SortOrder__c FROM RollupOrderBys__r), + (SELECT Id, DeveloperName, FieldName__c, NullSortOrder__c, Ranking__c, SortOrder__c FROM RollupOrderBys__r), RollupGrouping__r.Id, RollupGrouping__r.RollupOperation__c FROM Rollup__mdt @@ -149,7 +149,6 @@ 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; diff --git a/sfdx-project.json b/sfdx-project.json index 2ae33484..5e4f1ded 100644 --- a/sfdx-project.json +++ b/sfdx-project.json @@ -5,8 +5,8 @@ "package": "apex-rollup", "path": "rollup", "scopeProfiles": true, - "versionName": "Fixes parentRecordIdForEmptyChildrenCollections flow case", - "versionNumber": "1.6.34.0", + "versionName": "Fixes Rollup Order By sorting to always be deterministic, allows text templates in Full Recalc CMDT-driven Invocable invocable", + "versionNumber": "1.6.35.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": { @@ -101,12 +101,10 @@ "Apex Rollup - Rollup Callback@0.0.3-0": "04t6g000008Sis0AAC", "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.34": "04t6g000008OfJfAAK" + "apex-rollup@1.6.34": "04t6g000008OfJfAAK", + "apex-rollup@1.6.35": "04t6g000008OfKiAAK" } }