diff --git a/README.md b/README.md index 42104e1a..4a3a48cf 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 @@ -453,12 +453,15 @@ global static void runFromCDCTrigger() // imperatively from Apex, relying on CMDT for additional rollup info // if you are actually using this from WITHIN a trigger, the second argument should -// ALWAYS be the "Trigger.operationType" static variable -global static void runFromApex(List calcItems, TriggerOperation rollupContext) +// ALWAYS be the "Trigger.operationType" static variable (unless you're unit testing) +global static void runFromApex(List children, TriggerOperation rollupContext) + +// overload of the above, with support for the Trigger.oldMap variable (or your unit-tested approximation thereof) +global static void runFromApex(List children, Map oldChildrenMap, TriggerOperation rollupContext) // for more info on how this method differs from the one above it, check out the "Parent Level Merges" section! // for anything OTHER than merge situations use of this method is an anti-pattern -global static Rollup runFromApex(List rollupMetadata, Evaluator eval, List calcItems, Map oldCalcItems) +global static Rollup runFromApex(List rollupMetadata, Evaluator eval, List children, Map oldChildrenMap) // imperatively from Apex with arguments taking the place of values previously supplied by CMDT // can be used in conjunction with "batch" to group rollup operations (as seen in the example preceding this section) diff --git a/extra-tests/classes/RollupIntegrationTests.cls b/extra-tests/classes/RollupIntegrationTests.cls index eabee044..5ec7919d 100644 --- a/extra-tests/classes/RollupIntegrationTests.cls +++ b/extra-tests/classes/RollupIntegrationTests.cls @@ -1625,7 +1625,6 @@ private class RollupIntegrationTests { new ContactPointAddress(Id = RollupTestUtils.createId(ContactPointAddress.SObjectType), ParentId = acc.Id, PreferenceRank = 10) }; - Rollup.records = cpas; Rollup.rollupMetadata = new List{ new Rollup__mdt( RollupFieldOnCalcItem__c = 'PreferenceRank', @@ -1646,6 +1645,42 @@ private class RollupIntegrationTests { System.assertEquals(15, acc.AnnualRevenue); } + @IsTest + static void shouldRunWithOldRecordsDirectlyFromApex() { + Account acc = [SELECT Id FROM Account]; + + List cpas = new List{ + new ContactPointAddress(Id = RollupTestUtils.createId(ContactPointAddress.SObjectType), ParentId = acc.Id, PreferenceRank = 5), + new ContactPointAddress(Id = RollupTestUtils.createId(ContactPointAddress.SObjectType), ParentId = acc.Id, PreferenceRank = 10) + }; + + Rollup.rollupMetadata = new List{ + new Rollup__mdt( + RollupFieldOnCalcItem__c = 'PreferenceRank', + LookupObject__c = 'Account', + LookupFieldOnCalcItem__c = 'ParentId', + LookupFieldOnLookupObject__c = 'Id', + RollupFieldOnLookupObject__c = 'AnnualRevenue', + RollupOperation__c = 'SUM', + CalcItem__c = 'ContactPointAddress' + ) + }; + + Test.startTest(); + Rollup.runFromApex( + cpas, + new Map{ + cpas[0].Id => new ContactPointAddress(Id = cpas[0].Id, ParentId = acc.Id, PreferenceRank = 0), + cpas[1].Id => new ContactPointAddress(Id = cpas[1].Id, ParentId = acc.Id, PreferenceRank = 0) + }, + TriggerOperation.AFTER_UPDATE + ); + Test.stopTest(); + + acc = [SELECT AnnualRevenue FROM Account]; + System.assertEquals(15, acc.AnnualRevenue); + } + @IsTest static void shouldDeferUpdateWhenMaxParentRowsLessThanCurrentUpdateRows() { Account acc = [SELECT Id FROM Account]; diff --git a/package.json b/package.json index 534ad408..b4684684 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "apex-rollup", - "version": "1.6.31", + "version": "1.6.32", "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/rollup-namespaced/README.md b/rollup-namespaced/README.md index 8d3f723d..d8d8992b 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 221cb804..b351d95a 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 issue where sync rollups with lowercase field names in where clauses would not start up properly", - "versionNumber": "1.1.24.0", + "versionName": "Adds imperative Apex method Rollup.runFromApex for new and old record code paths", + "versionNumber": "1.1.25.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": { @@ -27,6 +27,7 @@ "apex-rollup-namespaced@1.1.21": "04t6g000008ObLCAA0", "apex-rollup-namespaced@1.1.22": "04t6g000008ObNDAA0", "apex-rollup-namespaced@1.1.23": "04t6g000008ObNSAA0", - "apex-rollup-namespaced@1.1.24": "04t6g000008ObbqAAC" + "apex-rollup-namespaced@1.1.24": "04t6g000008ObbqAAC", + "apex-rollup-namespaced@1.1.25": "04t6g000008Obc0AAC" } -} \ No newline at end of file +} diff --git a/rollup/core/classes/Rollup.cls b/rollup/core/classes/Rollup.cls index 397f09d2..4cbfd779 100644 --- a/rollup/core/classes/Rollup.cls +++ b/rollup/core/classes/Rollup.cls @@ -5,7 +5,7 @@ global without sharing virtual class Rollup implements RollupLogger.ToStringObje * but this keeps things much simpler */ @TestVisible - private static Boolean shouldRun = false; + private static Boolean shouldRun; @TestVisible private static TriggerOperation apexContext; @TestVisible @@ -1699,12 +1699,9 @@ global without sharing virtual class Rollup implements RollupLogger.ToStringObje * - TriggerOperation.BEFORE_DELETE * - TriggerOperation.AFTER_UNDELETE * - * Note that while it's an anti-pattern to call this from directly within a Trigger, - * because `runFromTrigger()` coupled with CMDT is the better option, - * you should pass: `Trigger.operationType` for the second argument if you do. */ - global static void runFromApex(List calcItems, TriggerOperation rollupContext) { - shouldRun = true; + global static void runFromApex(List calcItems, System.TriggerOperation rollupContext) { + shouldRun = shouldRun ?? true; records = calcItems; apexContext = rollupContext; @@ -1715,6 +1712,12 @@ global without sharing virtual class Rollup implements RollupLogger.ToStringObje apexContext = null; } + global static void runFromApex(List children, Map oldChildrenMap, System.TriggerOperation rollupContext) { + oldChildrenMap = oldChildrenMap; + runFromApex(children, rollupContext); + oldChildrenMap = null; + } + global static Rollup runFromApex(List localMetas, Evaluator eval, List calcItems, Map oldCalcItems) { Rollup rollupConductor = RollupAsyncProcessor.getConductor(InvocationPoint.FROM_APEX, calcItems, oldCalcItems); if (shouldRunFromTrigger() == false || localMetas.isEmpty()) { @@ -2828,7 +2831,7 @@ global without sharing virtual class Rollup implements RollupLogger.ToStringObje * because the underlying SObject can't be modified till afterwards */ private static Boolean shouldRunFromTrigger() { - shouldRun = shouldRun || Trigger.isExecuting; + shouldRun = Trigger.isExecuting || (shouldRun ?? false); if (Trigger.operationType != null && isCDC == false) { apexContext = Trigger.operationType; } diff --git a/rollup/core/classes/RollupLogger.cls b/rollup/core/classes/RollupLogger.cls index 41701573..b623150f 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.31'; + private static final String CURRENT_VERSION_NUMBER = 'v1.6.32'; private static final System.LoggingLevel FALLBACK_LOGGING_LEVEL = System.LoggingLevel.DEBUG; private static final RollupPlugin PLUGIN = new RollupPlugin(); diff --git a/sfdx-project.json b/sfdx-project.json index 8114c81e..9703a20d 100644 --- a/sfdx-project.json +++ b/sfdx-project.json @@ -5,8 +5,8 @@ "package": "apex-rollup", "path": "rollup", "scopeProfiles": true, - "versionName": "Fixes issue where sync rollups with lowercase field names in where clauses would not start up properly", - "versionNumber": "1.6.31.0", + "versionName": "Adds imperative Apex method Rollup.runFromApex for new and old record code paths", + "versionNumber": "1.6.32.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,10 +101,10 @@ "Apex Rollup - Rollup Callback@0.0.3-0": "04t6g000008Sis0AAC", "Nebula Logger - Core@4.8.0-NEXT-ignore-origin-method": "04t5Y0000015lslQAA", "apex-rollup": "0Ho6g000000TNcOCAW", - "apex-rollup@1.6.27": "04t6g000008ObL7AAK", "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.31": "04t6g000008ObblAAC", + "apex-rollup@1.6.32": "04t6g000008ObbvAAC" } -} \ No newline at end of file +}