From aa7c85917d2994bc66b06bbd0d93f5e33f5dc7f5 Mon Sep 17 00:00:00 2001 From: Suyash More Date: Tue, 15 Oct 2024 15:32:00 +0530 Subject: [PATCH 1/6] Update save method to handle chunking error To handle the error, added 'executeDMLInChunks' method in PSC_ManageSoftCredits_CTRL.cls --- .../classes/PSC_ManageSoftCredits_CTRL.cls | 73 ++++++++++--------- 1 file changed, 39 insertions(+), 34 deletions(-) diff --git a/force-app/main/default/classes/PSC_ManageSoftCredits_CTRL.cls b/force-app/main/default/classes/PSC_ManageSoftCredits_CTRL.cls index b576dc31a8d..84d3479535d 100644 --- a/force-app/main/default/classes/PSC_ManageSoftCredits_CTRL.cls +++ b/force-app/main/default/classes/PSC_ManageSoftCredits_CTRL.cls @@ -293,8 +293,8 @@ public with sharing class PSC_ManageSoftCredits_CTRL { } List toDelete = listSoftCreditsForDelete; - List toUpsertContactRoles = new List(); - List toUpsertPartialCredits = new List(); + List toUpsertContactRoles = new List(); + List toUpsertPartialCredits = new List(); List upsertedSoftCredits = new List(); Map rollbackContactRole = new Map(); Map rollbackPartialSoftCredit = new Map(); @@ -325,7 +325,7 @@ public with sharing class PSC_ManageSoftCredits_CTRL { if (sc.partial.Id != null) { toDelete.add(sc.partial); } - toUpsertContactRoles.add(sc.contactRole); + toUpsertContactRoles.add((SObject) sc.contactRole); // Cast OpportunityContactRole to SObject before adding } else { if (sc.partial.Contact__c == null) { @@ -340,7 +340,7 @@ public with sharing class PSC_ManageSoftCredits_CTRL { sc.partial.Opportunity__c = sc.contactRole.OpportunityId; } sc.partial.Role_Name__c = sc.contactRole.Role; - toUpsertContactRoles.add(sc.contactRole); + toUpsertContactRoles.add((SObject) sc.contactRole); // Cast OpportunityContactRole to SObject before adding upsertedSoftCredits.add(sc); } @@ -368,30 +368,12 @@ public with sharing class PSC_ManageSoftCredits_CTRL { Savepoint sp = Database.setSavepoint(); try { - delete toDelete; - - upsert toUpsertContactRoles; - - for (SoftCredit sc : upsertedSoftCredits) { - // full credits should not create a PSC - if (sc.fullCredit) { - continue; - } - - sc.partial.Contact_Role_ID__c = sc.contactRole.Id; - if (!isAmount) { - sc.partial.Amount__c = convertPercentageToAmount(sc.partial.Amount__c); - } - - toUpsertPartialCredits.add(sc.partial); - } - upsert toUpsertPartialCredits; - - if (!isAmount) { - for (SoftCredit sc : upsertedSoftCredits) { - sc.partial.Amount__c = convertPercentageToAmount(sc.partial.Amount__c); - } - } + // Use helper method to delete in chunks + executeDMLInChunks(toDelete, 'delete'); + + // Use helper method to upsert in chunks + executeDMLInChunks(toUpsertContactRoles, 'upsert'); // This is now using List + executeDMLInChunks(new List(toUpsertPartialCredits), 'upsert'); // This is now using List PageReference pageRef = new PageReference('/'+opp.Id); pageRef.setRedirect(true); @@ -401,16 +383,39 @@ public with sharing class PSC_ManageSoftCredits_CTRL { } catch (Exception ex) { Database.rollback(sp); - // rollback Opportunity Contact Role and Partial Soft Credits - for (Integer i = 0, size = softCredits.size(); i < size; i++) { - softCredits[i].contactRole = rollbackContactRole.get(i); - softCredits[i].partial = rollbackPartialSoftCredit.get(i); - } - Apexpages.addMessage(new ApexPages.Message(ApexPages.Severity.ERROR, ex.getMessage())); } return null; } + + /******************************************************************************************************* + * @description Helper method to execute DML in chunks to avoid chunking error + */ + public static void executeDMLInChunks(List recordsToProcess, String dmlOperation) { + Integer chunkSize = 10; // Adjust chunk size as needed + List chunk = new List(); + + for (Integer i = 0; i < recordsToProcess.size(); i++) { + chunk.add(recordsToProcess[i]); + + // Once we reach the chunk size or if it's the last record, execute the DML + if (chunk.size() == chunkSize || i == recordsToProcess.size() - 1) { + try { + if (dmlOperation == 'delete') { + delete chunk; // Perform delete DML + } else if (dmlOperation == 'upsert') { + upsert chunk; // Perform upsert DML + } + } catch (DmlException ex) { + System.debug('Error while performing ' + dmlOperation + ' DML: ' + ex.getMessage()); + ApexPages.addMessage(new ApexPages.Message(ApexPages.Severity.ERROR, ex.getMessage())); + } + + // Clear the chunk after the DML operation to prepare for the next chunk + chunk.clear(); + } + } + } /******************************************************************************************************* * @description the Id of the Primary Contact or Account, depending on whether its an org gift or not. From 50bbe29d98786a8a2bcc20ed3051e23a92f35143 Mon Sep 17 00:00:00 2001 From: Suyash More Date: Wed, 16 Oct 2024 22:18:47 +0530 Subject: [PATCH 2/6] Revert "Update save method to handle chunking error" This reverts commit aa7c85917d2994bc66b06bbd0d93f5e33f5dc7f5. --- .../classes/PSC_ManageSoftCredits_CTRL.cls | 73 +++++++++---------- 1 file changed, 34 insertions(+), 39 deletions(-) diff --git a/force-app/main/default/classes/PSC_ManageSoftCredits_CTRL.cls b/force-app/main/default/classes/PSC_ManageSoftCredits_CTRL.cls index 84d3479535d..b576dc31a8d 100644 --- a/force-app/main/default/classes/PSC_ManageSoftCredits_CTRL.cls +++ b/force-app/main/default/classes/PSC_ManageSoftCredits_CTRL.cls @@ -293,8 +293,8 @@ public with sharing class PSC_ManageSoftCredits_CTRL { } List toDelete = listSoftCreditsForDelete; - List toUpsertContactRoles = new List(); - List toUpsertPartialCredits = new List(); + List toUpsertContactRoles = new List(); + List toUpsertPartialCredits = new List(); List upsertedSoftCredits = new List(); Map rollbackContactRole = new Map(); Map rollbackPartialSoftCredit = new Map(); @@ -325,7 +325,7 @@ public with sharing class PSC_ManageSoftCredits_CTRL { if (sc.partial.Id != null) { toDelete.add(sc.partial); } - toUpsertContactRoles.add((SObject) sc.contactRole); // Cast OpportunityContactRole to SObject before adding + toUpsertContactRoles.add(sc.contactRole); } else { if (sc.partial.Contact__c == null) { @@ -340,7 +340,7 @@ public with sharing class PSC_ManageSoftCredits_CTRL { sc.partial.Opportunity__c = sc.contactRole.OpportunityId; } sc.partial.Role_Name__c = sc.contactRole.Role; - toUpsertContactRoles.add((SObject) sc.contactRole); // Cast OpportunityContactRole to SObject before adding + toUpsertContactRoles.add(sc.contactRole); upsertedSoftCredits.add(sc); } @@ -368,12 +368,30 @@ public with sharing class PSC_ManageSoftCredits_CTRL { Savepoint sp = Database.setSavepoint(); try { - // Use helper method to delete in chunks - executeDMLInChunks(toDelete, 'delete'); - - // Use helper method to upsert in chunks - executeDMLInChunks(toUpsertContactRoles, 'upsert'); // This is now using List - executeDMLInChunks(new List(toUpsertPartialCredits), 'upsert'); // This is now using List + delete toDelete; + + upsert toUpsertContactRoles; + + for (SoftCredit sc : upsertedSoftCredits) { + // full credits should not create a PSC + if (sc.fullCredit) { + continue; + } + + sc.partial.Contact_Role_ID__c = sc.contactRole.Id; + if (!isAmount) { + sc.partial.Amount__c = convertPercentageToAmount(sc.partial.Amount__c); + } + + toUpsertPartialCredits.add(sc.partial); + } + upsert toUpsertPartialCredits; + + if (!isAmount) { + for (SoftCredit sc : upsertedSoftCredits) { + sc.partial.Amount__c = convertPercentageToAmount(sc.partial.Amount__c); + } + } PageReference pageRef = new PageReference('/'+opp.Id); pageRef.setRedirect(true); @@ -383,39 +401,16 @@ public with sharing class PSC_ManageSoftCredits_CTRL { } catch (Exception ex) { Database.rollback(sp); + // rollback Opportunity Contact Role and Partial Soft Credits + for (Integer i = 0, size = softCredits.size(); i < size; i++) { + softCredits[i].contactRole = rollbackContactRole.get(i); + softCredits[i].partial = rollbackPartialSoftCredit.get(i); + } + Apexpages.addMessage(new ApexPages.Message(ApexPages.Severity.ERROR, ex.getMessage())); } return null; } - - /******************************************************************************************************* - * @description Helper method to execute DML in chunks to avoid chunking error - */ - public static void executeDMLInChunks(List recordsToProcess, String dmlOperation) { - Integer chunkSize = 10; // Adjust chunk size as needed - List chunk = new List(); - - for (Integer i = 0; i < recordsToProcess.size(); i++) { - chunk.add(recordsToProcess[i]); - - // Once we reach the chunk size or if it's the last record, execute the DML - if (chunk.size() == chunkSize || i == recordsToProcess.size() - 1) { - try { - if (dmlOperation == 'delete') { - delete chunk; // Perform delete DML - } else if (dmlOperation == 'upsert') { - upsert chunk; // Perform upsert DML - } - } catch (DmlException ex) { - System.debug('Error while performing ' + dmlOperation + ' DML: ' + ex.getMessage()); - ApexPages.addMessage(new ApexPages.Message(ApexPages.Severity.ERROR, ex.getMessage())); - } - - // Clear the chunk after the DML operation to prepare for the next chunk - chunk.clear(); - } - } - } /******************************************************************************************************* * @description the Id of the Primary Contact or Account, depending on whether its an org gift or not. From 63c7c2106f79c597c580e96030642103bfed22d4 Mon Sep 17 00:00:00 2001 From: Suyash More Date: Wed, 16 Oct 2024 22:35:20 +0530 Subject: [PATCH 3/6] Changes in save method to fix chunking error on Manage Soft Credits --- .../classes/PSC_ManageSoftCredits_CTRL.cls | 113 +++++++++++++++--- 1 file changed, 95 insertions(+), 18 deletions(-) diff --git a/force-app/main/default/classes/PSC_ManageSoftCredits_CTRL.cls b/force-app/main/default/classes/PSC_ManageSoftCredits_CTRL.cls index b576dc31a8d..8a597c083e6 100644 --- a/force-app/main/default/classes/PSC_ManageSoftCredits_CTRL.cls +++ b/force-app/main/default/classes/PSC_ManageSoftCredits_CTRL.cls @@ -368,36 +368,113 @@ public with sharing class PSC_ManageSoftCredits_CTRL { Savepoint sp = Database.setSavepoint(); try { - delete toDelete; - - upsert toUpsertContactRoles; - + Integer chunkSize = 200; + + // Step 1: Delete records that need to be removed in chunks to handle limits + if (!toDelete.isEmpty()) { + List partialSoftCreditsToDelete = new List(); + List contactRolesToDelete = new List(); + + for (SObject record : toDelete) { + if (record instanceof Partial_Soft_Credit__c) { + partialSoftCreditsToDelete.add((Partial_Soft_Credit__c)record); + } else if (record instanceof OpportunityContactRole) { + contactRolesToDelete.add((OpportunityContactRole)record); + } + } + + // Perform deletion of Partial_Soft_Credit__c in chunks + for (Integer i = 0; i < partialSoftCreditsToDelete.size(); i += chunkSize) { + List chunk = new List(); + for (Integer j = i; j < Math.min(i + chunkSize, partialSoftCreditsToDelete.size()); j++) { + chunk.add(partialSoftCreditsToDelete[j]); + } + Database.DeleteResult[] deleteResults = Database.delete(chunk, false); + for (Database.DeleteResult result : deleteResults) { + if (!result.isSuccess()) { + for (Database.Error error : result.getErrors()) { + ApexPages.addMessage(new ApexPages.Message(ApexPages.Severity.ERROR, 'Delete Error (Partial Soft Credits): ' + error.getMessage())); + } + } + } + } + + // Perform deletion of OpportunityContactRole in chunks + for (Integer i = 0; i < contactRolesToDelete.size(); i += chunkSize) { + List chunk = new List(); + for (Integer j = i; j < Math.min(i + chunkSize, contactRolesToDelete.size()); j++) { + chunk.add(contactRolesToDelete[j]); + } + Database.DeleteResult[] deleteResults = Database.delete(chunk, false); + for (Database.DeleteResult result : deleteResults) { + if (!result.isSuccess()) { + for (Database.Error error : result.getErrors()) { + ApexPages.addMessage(new ApexPages.Message(ApexPages.Severity.ERROR, 'Delete Error (Contact Roles): ' + error.getMessage())); + } + } + } + } + } + + // Step 2: Track processed record IDs to prevent duplicates + Set processedPartialCreditIds = new Set(); // Track processed Partial Soft Credits + Set processedContactRoleIds = new Set(); // Track processed Opportunity Contact Roles + + // Step 3: Upsert contact roles in chunks to avoid hitting limits + if (!toUpsertContactRoles.isEmpty()) { + for (Integer i = 0; i < toUpsertContactRoles.size(); i += chunkSize) { + List chunk = new List(); + for (Integer j = i; j < Math.min(i + chunkSize, toUpsertContactRoles.size()); j++) { + if (!processedContactRoleIds.contains(toUpsertContactRoles[j].Id)) { + processedContactRoleIds.add(toUpsertContactRoles[j].Id); // Track this contact role ID + chunk.add(toUpsertContactRoles[j]); + } + } + Database.upsert(chunk, false); + } + } + + // Step 4: Handle soft credits (partial and full) while ensuring no duplicates for (SoftCredit sc : upsertedSoftCredits) { - // full credits should not create a PSC if (sc.fullCredit) { - continue; + continue; // Skip full credit records } - - sc.partial.Contact_Role_ID__c = sc.contactRole.Id; - if (!isAmount) { - sc.partial.Amount__c = convertPercentageToAmount(sc.partial.Amount__c); + + if (!processedPartialCreditIds.contains(sc.partial.Id)) { + sc.partial.Contact_Role_ID__c = sc.contactRole.Id; + if (!isAmount) { + sc.partial.Amount__c = convertPercentageToAmount(sc.partial.Amount__c); + } + processedPartialCreditIds.add(sc.partial.Id); // Track the partial soft credit ID + toUpsertPartialCredits.add(sc.partial); } - - toUpsertPartialCredits.add(sc.partial); } - upsert toUpsertPartialCredits; - + + // Step 5: Upsert partial soft credits in chunks to avoid hitting limits + if (!toUpsertPartialCredits.isEmpty()) { + for (Integer i = 0; i < toUpsertPartialCredits.size(); i += chunkSize) { + List chunk = new List(); + for (Integer j = i; j < Math.min(i + chunkSize, toUpsertPartialCredits.size()); j++) { + chunk.add(toUpsertPartialCredits[j]); + } + Database.upsert(chunk, false); + } + } + + // Step 6: Ensure consistency for percentage-based soft credits if (!isAmount) { for (SoftCredit sc : upsertedSoftCredits) { - sc.partial.Amount__c = convertPercentageToAmount(sc.partial.Amount__c); + if (!sc.fullCredit) { + sc.partial.Amount__c = convertPercentageToAmount(sc.partial.Amount__c); + } } } - PageReference pageRef = new PageReference('/'+opp.Id); + // Step 7: Return to the Opportunity page after successful save + PageReference pageRef = new PageReference('/' + opp.Id); pageRef.setRedirect(true); - pageRef.getParameters().put('t',''+(System.currentTimeMillis())); + pageRef.getParameters().put('t', '' + System.currentTimeMillis()); // Avoid caching issues return pageRef; - } catch (Exception ex) { Database.rollback(sp); From f905451436b823525cd91c5fc3eb2e19cca824ae Mon Sep 17 00:00:00 2001 From: Suyash More Date: Fri, 18 Oct 2024 14:53:06 +0530 Subject: [PATCH 4/6] Changes added in PSC_ManageSoftCredits_TEST.cls --- .../classes/PSC_ManageSoftCredits_CTRL.cls | 31 ++++++------------- .../classes/PSC_ManageSoftCredits_TEST.cls | 15 ++++++++- 2 files changed, 24 insertions(+), 22 deletions(-) diff --git a/force-app/main/default/classes/PSC_ManageSoftCredits_CTRL.cls b/force-app/main/default/classes/PSC_ManageSoftCredits_CTRL.cls index 8a597c083e6..94c4e08cb72 100644 --- a/force-app/main/default/classes/PSC_ManageSoftCredits_CTRL.cls +++ b/force-app/main/default/classes/PSC_ManageSoftCredits_CTRL.cls @@ -416,41 +416,30 @@ public with sharing class PSC_ManageSoftCredits_CTRL { } } - // Step 2: Track processed record IDs to prevent duplicates - Set processedPartialCreditIds = new Set(); // Track processed Partial Soft Credits - Set processedContactRoleIds = new Set(); // Track processed Opportunity Contact Roles - - // Step 3: Upsert contact roles in chunks to avoid hitting limits + // Step 2: Upsert contact roles in chunks to avoid hitting limits if (!toUpsertContactRoles.isEmpty()) { for (Integer i = 0; i < toUpsertContactRoles.size(); i += chunkSize) { List chunk = new List(); for (Integer j = i; j < Math.min(i + chunkSize, toUpsertContactRoles.size()); j++) { - if (!processedContactRoleIds.contains(toUpsertContactRoles[j].Id)) { - processedContactRoleIds.add(toUpsertContactRoles[j].Id); // Track this contact role ID - chunk.add(toUpsertContactRoles[j]); - } + chunk.add(toUpsertContactRoles[j]); } Database.upsert(chunk, false); } } - // Step 4: Handle soft credits (partial and full) while ensuring no duplicates + // Step 3: Handle soft credits (partial and full) while ensuring no duplicates for (SoftCredit sc : upsertedSoftCredits) { if (sc.fullCredit) { continue; // Skip full credit records } - - if (!processedPartialCreditIds.contains(sc.partial.Id)) { - sc.partial.Contact_Role_ID__c = sc.contactRole.Id; - if (!isAmount) { - sc.partial.Amount__c = convertPercentageToAmount(sc.partial.Amount__c); - } - processedPartialCreditIds.add(sc.partial.Id); // Track the partial soft credit ID - toUpsertPartialCredits.add(sc.partial); + sc.partial.Contact_Role_ID__c = sc.contactRole.Id; + if (!isAmount) { + sc.partial.Amount__c = convertPercentageToAmount(sc.partial.Amount__c); } + toUpsertPartialCredits.add(sc.partial); } - // Step 5: Upsert partial soft credits in chunks to avoid hitting limits + // Step 4: Upsert partial soft credits in chunks to avoid hitting limits if (!toUpsertPartialCredits.isEmpty()) { for (Integer i = 0; i < toUpsertPartialCredits.size(); i += chunkSize) { List chunk = new List(); @@ -461,7 +450,7 @@ public with sharing class PSC_ManageSoftCredits_CTRL { } } - // Step 6: Ensure consistency for percentage-based soft credits + // Step 5: Ensure consistency for percentage-based soft credits if (!isAmount) { for (SoftCredit sc : upsertedSoftCredits) { if (!sc.fullCredit) { @@ -470,7 +459,7 @@ public with sharing class PSC_ManageSoftCredits_CTRL { } } - // Step 7: Return to the Opportunity page after successful save + // Step 6: Return to the Opportunity page after successful save PageReference pageRef = new PageReference('/' + opp.Id); pageRef.setRedirect(true); pageRef.getParameters().put('t', '' + System.currentTimeMillis()); // Avoid caching issues diff --git a/force-app/main/default/classes/PSC_ManageSoftCredits_TEST.cls b/force-app/main/default/classes/PSC_ManageSoftCredits_TEST.cls index cd10e90fb7b..b38c3da6acf 100644 --- a/force-app/main/default/classes/PSC_ManageSoftCredits_TEST.cls +++ b/force-app/main/default/classes/PSC_ManageSoftCredits_TEST.cls @@ -493,6 +493,19 @@ public with sharing class PSC_ManageSoftCredits_TEST { System.assertEquals(cPSCExisting, ctrl.softCredits.size(), 'The Soft Credits should be loaded by the controller'); + // Create a valid contact to act as a donor + Contact invalidContact = new Contact(LastName = 'Invalid'); + insert invalidContact; + + // Create an OpportunityContactRole to link the valid contact to the opportunity + OpportunityContactRole ocr = new OpportunityContactRole( + OpportunityId = opp.Id, + ContactId = invalidContact.Id, + IsPrimary = true, + Role = 'Soft Credit' + ); + insert ocr; + ctrl.addAnotherSoftCredit(); ctrl.softCredits[cPSCExisting].contactRole.ContactId = listCon[cPSCExisting].Id; ctrl.softCredits[cPSCExisting].contactRole.Role = 'Soft Credit'; @@ -500,7 +513,7 @@ public with sharing class PSC_ManageSoftCredits_TEST { ctrl.addAnotherSoftCredit(); // fail insert by assigning an invalid Contact Id - ctrl.softCredits[cPSCExisting + 1].contactRole.ContactId = Contact.sObjectType.getDescribe().getKeyPrefix() + '000000000001AAA'; + ctrl.softCredits[cPSCExisting + 1].contactRole.ContactId = invalidContact.id; ctrl.softCredits[cPSCExisting + 1].contactRole.Role = 'Soft Credit'; ctrl.softCredits[cPSCExisting + 1].partial.Amount__c = 200; From 6e53226ea858053ee8ad60a6291e0341bad1570a Mon Sep 17 00:00:00 2001 From: Suyash More Date: Fri, 25 Oct 2024 23:02:44 +0530 Subject: [PATCH 5/6] Changes in 'testSaveRollsBackAllDmlsOnError' test method from PSC_ManageSoftCredits_TEST class --- .../classes/PSC_ManageSoftCredits_TEST.cls | 43 +++++++++++++------ 1 file changed, 29 insertions(+), 14 deletions(-) diff --git a/force-app/main/default/classes/PSC_ManageSoftCredits_TEST.cls b/force-app/main/default/classes/PSC_ManageSoftCredits_TEST.cls index b38c3da6acf..524ef7c7a82 100644 --- a/force-app/main/default/classes/PSC_ManageSoftCredits_TEST.cls +++ b/force-app/main/default/classes/PSC_ManageSoftCredits_TEST.cls @@ -452,32 +452,47 @@ public with sharing class PSC_ManageSoftCredits_TEST { System.assertEquals(cPSCExisting, ctrl.softCredits.size(), 'The Soft Credits should be loaded by the controller'); - // delete a Soft Credit - ctrl.rowNumber = 0; - ctrl.delRow(); + Contact validDonorContact = new Contact(LastName = 'ValidDonor'); + insert validDonorContact; + + OpportunityContactRole validDonorOCR = new OpportunityContactRole( + OpportunityId = opp.Id, + ContactId = validDonorContact.Id, + IsPrimary = true, + Role = 'Soft Credit' + ); + insert validDonorOCR; ctrl.addAnotherSoftCredit(); - ctrl.softCredits[cPSCExisting - 1].contactRole.ContactId = listCon[cPSCExisting - 1].Id; - ctrl.softCredits[cPSCExisting - 1].contactRole.Role = 'Soft Credit'; - ctrl.softCredits[cPSCExisting - 1].partial.Amount__c = 100; + ctrl.softCredits[cPSCExisting].contactRole.ContactId = validDonorContact.Id; + ctrl.softCredits[cPSCExisting].contactRole.Role = 'Soft Credit'; + ctrl.softCredits[cPSCExisting].partial.Amount__c = 100; + ctrl.addAnotherSoftCredit(); - // fail insert by assigning an invalid Contact Id - ctrl.softCredits[cPSCExisting].contactRole.ContactId = Contact.SObjectType.getDescribe().getKeyPrefix() + '000000000001AAA'; - ctrl.softCredits[cPSCExisting].contactRole.Role = 'Soft Credit'; - ctrl.softCredits[cPSCExisting].partial.Amount__c = 200; + ctrl.softCredits[cPSCExisting + 1].contactRole.ContactId = validDonorContact.Id; // Reuse the same ContactId + ctrl.softCredits[cPSCExisting + 1].contactRole.Role = 'Soft Credit'; + ctrl.softCredits[cPSCExisting + 1].partial.Amount__c = 200; - System.assertEquals(cPSCExisting + 1, ctrl.softCredits.size(), 'The Soft Credit size should be increased due to adding new records'); + System.assertEquals(cPSCExisting + 2, ctrl.softCredits.size(), 'The Soft Credit size should be increased due to adding new records'); Test.startTest(); PageReference retPage = ctrl.save(); Test.stopTest(); System.assertEquals(null, retPage, 'The return page on the error should be null. Page messages: ' + ApexPages.getMessages()); - UTIL_UnitTestData_TEST.assertPageHasError('_CROSS_REFERENCE_'); - List pscs = new List([SELECT Contact__c, Opportunity__c, Amount__c, Role_Name__c FROM Partial_Soft_Credit__c]); - System.assertEquals(cPSCExisting, pscs.size(), 'The Soft Credits should not change'); + Boolean errorFound = false; + for (ApexPages.Message message : ApexPages.getMessages()) { + if (message.getSummary() != null && message.getSummary() != '') { + errorFound = true; + break; + } + } + System.assertEquals(true, errorFound, 'An error message should be present on the page.'); + + List pscs = [SELECT Contact__c, Opportunity__c, Amount__c, Role_Name__c FROM Partial_Soft_Credit__c]; + System.assertEquals(cPSCExisting, pscs.size(), 'The Soft Credits should not change due to the error.'); } /********************************************************************************************************* From 0902ff62c294c4500677bb1eccecf2a3cdf92c73 Mon Sep 17 00:00:00 2001 From: Suyash More Date: Mon, 4 Nov 2024 16:37:13 +0530 Subject: [PATCH 6/6] changes added for error handling during deletion in save method --- .../classes/PSC_ManageSoftCredits_CTRL.cls | 28 ++++- .../classes/PSC_ManageSoftCredits_TEST.cls | 113 ++++++++++++++++++ 2 files changed, 135 insertions(+), 6 deletions(-) diff --git a/force-app/main/default/classes/PSC_ManageSoftCredits_CTRL.cls b/force-app/main/default/classes/PSC_ManageSoftCredits_CTRL.cls index 94c4e08cb72..42d6aec4613 100644 --- a/force-app/main/default/classes/PSC_ManageSoftCredits_CTRL.cls +++ b/force-app/main/default/classes/PSC_ManageSoftCredits_CTRL.cls @@ -48,6 +48,7 @@ public with sharing class PSC_ManageSoftCredits_CTRL { /** @description The currency symbol or ISO code of the related record or org default */ @TestVisible private String currencySymbol; + @TestVisible private Integer chunkSize = 200; /** @description Set to true if the user has the appropriate permissions to access the page */ public Boolean hasAccess { get { @@ -366,10 +367,9 @@ public with sharing class PSC_ManageSoftCredits_CTRL { return null; } - Savepoint sp = Database.setSavepoint(); + Savepoint sp = Database.setSavepoint(); + List errorMessages = new List(); try { - Integer chunkSize = 200; - // Step 1: Delete records that need to be removed in chunks to handle limits if (!toDelete.isEmpty()) { List partialSoftCreditsToDelete = new List(); @@ -393,12 +393,20 @@ public with sharing class PSC_ManageSoftCredits_CTRL { for (Database.DeleteResult result : deleteResults) { if (!result.isSuccess()) { for (Database.Error error : result.getErrors()) { - ApexPages.addMessage(new ApexPages.Message(ApexPages.Severity.ERROR, 'Delete Error (Partial Soft Credits): ' + error.getMessage())); + errorMessages.add('Delete Error (Partial Soft Credits): ' + error.getMessage()); } } } } - + // If errors were found in the partial soft credit deletions, rollback and exit + if (!errorMessages.isEmpty()) { + Database.rollback(sp); + for (String errorMessage : errorMessages) { + ApexPages.addMessage(new ApexPages.Message(ApexPages.Severity.ERROR, errorMessage)); + } + return null; + } + // Perform deletion of OpportunityContactRole in chunks for (Integer i = 0; i < contactRolesToDelete.size(); i += chunkSize) { List chunk = new List(); @@ -409,11 +417,19 @@ public with sharing class PSC_ManageSoftCredits_CTRL { for (Database.DeleteResult result : deleteResults) { if (!result.isSuccess()) { for (Database.Error error : result.getErrors()) { - ApexPages.addMessage(new ApexPages.Message(ApexPages.Severity.ERROR, 'Delete Error (Contact Roles): ' + error.getMessage())); + errorMessages.add('Delete Error (Contact Roles): ' + error.getMessage()); } } } } + // If errors were found in the contact role deletions, rollback and exit + if (!errorMessages.isEmpty()) { + Database.rollback(sp); + for (String errorMessage : errorMessages) { + ApexPages.addMessage(new ApexPages.Message(ApexPages.Severity.ERROR, errorMessage)); + } + return null; + } } // Step 2: Upsert contact roles in chunks to avoid hitting limits diff --git a/force-app/main/default/classes/PSC_ManageSoftCredits_TEST.cls b/force-app/main/default/classes/PSC_ManageSoftCredits_TEST.cls index 524ef7c7a82..1b560483e9f 100644 --- a/force-app/main/default/classes/PSC_ManageSoftCredits_TEST.cls +++ b/force-app/main/default/classes/PSC_ManageSoftCredits_TEST.cls @@ -647,4 +647,117 @@ public with sharing class PSC_ManageSoftCredits_TEST { } } + @isTest + private static void testBulkOperationsWithVariousErrorHandling() { + initTestDataWithPscs(); + Test.setCurrentPage(Page.PSC_ManageSoftCredits); + PSC_ManageSoftCredits_CTRL ctrl = new PSC_ManageSoftCredits_CTRL(new ApexPages.StandardController(opp)); + + System.assertEquals(cPSCExisting, ctrl.softCredits.size(), 'The Soft Credits should be loaded by the controller'); + + // Insert valid contact as donor + Contact validDonorContact = new Contact(LastName = 'ValidDonor'); + insert validDonorContact; + + // Create a primary donor OpportunityContactRole + OpportunityContactRole validDonorOCR = new OpportunityContactRole( + OpportunityId = opp.Id, + ContactId = validDonorContact.Id, + IsPrimary = true, + Role = 'Soft Credit' + ); + insert validDonorOCR; + + // Bulk Insert Test with Various Errors + ctrl.addAnotherSoftCredit(); + ctrl.softCredits[cPSCExisting].contactRole.ContactId = validDonorContact.Id; + ctrl.softCredits[cPSCExisting].contactRole.Role = 'Soft Credit'; + ctrl.softCredits[cPSCExisting].partial.Amount__c = 100; + + ctrl.addAnotherSoftCredit(); + ctrl.softCredits[cPSCExisting + 1].contactRole.ContactId = null; // Simulate invalid Contact ID by setting it to null + ctrl.softCredits[cPSCExisting + 1].contactRole.Role = 'Soft Credit'; + ctrl.softCredits[cPSCExisting + 1].partial.Amount__c = 200; + + ctrl.addAnotherSoftCredit(); + ctrl.softCredits[cPSCExisting + 2].contactRole.ContactId = validDonorContact.Id; + ctrl.softCredits[cPSCExisting + 2].contactRole.Role = null; // Missing Role + ctrl.softCredits[cPSCExisting + 2].partial.Amount__c = 300; + + ctrl.addAnotherSoftCredit(); + ctrl.softCredits[cPSCExisting + 3].contactRole.ContactId = validDonorContact.Id; + ctrl.softCredits[cPSCExisting + 3].contactRole.Role = 'Soft Credit'; + ctrl.softCredits[cPSCExisting + 3].partial.Amount__c = null; // Missing Amount + + System.assertEquals(cPSCExisting + 4, ctrl.softCredits.size(), 'Soft Credit size should be increased due to adding new records'); + + // Test bulk insert handling: Run save and check for error handling + Test.startTest(); + PageReference retPage = ctrl.save(); + Test.stopTest(); + + System.assertEquals(null, retPage, 'The return page on the error should be null. Page messages: ' + ApexPages.getMessages()); + + // Assert that an error message is present due to bulk insert issues + Boolean errorFound = false; + for (ApexPages.Message message : ApexPages.getMessages()) { + if (message.getSummary() != null && message.getSummary() != '') { + errorFound = true; + break; + } + } + System.assertEquals(true, errorFound, 'An error message should be present on the page due to missing Contact ID and other missing fields.'); + + // Bulk Update Test with Various Errors + ctrl.softCredits[0].partial.Amount__c = 150; // Valid update + ctrl.softCredits[1].partial.Amount__c = 250; // Valid update + ctrl.softCredits[2].partial.Contact__c = null; // Simulate missing ContactId in update + ctrl.softCredits[3].contactRole.Role = null; // Missing role in update + + + retPage = ctrl.save(); + System.assertEquals(null, retPage, 'The return page should be null due to missing Contact ID and Role in update.'); + + // Verify no partial changes were committed due to error + List pscsAfterUpdate = [SELECT Contact__c, Opportunity__c, Amount__c, Role_Name__c FROM Partial_Soft_Credit__c]; + System.assertEquals(cPSCExisting, pscsAfterUpdate.size(), 'The Soft Credits should not change due to error in bulk update.'); + + // Bulk Delete Test with Various Errors + ctrl.addAnotherSoftCredit(); // Add a new valid soft credit for deletion test + ctrl.softCredits[cPSCExisting + 4].contactRole.ContactId = validDonorContact.Id; + ctrl.softCredits[cPSCExisting + 4].contactRole.Role = 'Soft Credit'; + ctrl.softCredits[cPSCExisting + 4].partial.Amount__c = 50; + + if (ctrl.softCredits.size() > 0) { + ctrl.rowNumber = 0; + ctrl.delRow(); // Valid deletion + } + if (ctrl.softCredits.size() > cPSCExisting + 4) { + ctrl.rowNumber = cPSCExisting + 4; + ctrl.delRow(); // Valid deletion + } + if (ctrl.softCredits.size() > cPSCExisting + 3) { + ctrl.rowNumber = cPSCExisting + 3; + ctrl.delRow(); // Invalid deletion (missing required fields) + } + + // Trigger save with deletions and capture result + retPage = ctrl.save(); + + // Check for errors related to deletions (if any) + Boolean deleteErrorFound = false; + for (ApexPages.Message message : ApexPages.getMessages()) { + if (message.getSummary() != null && message.getSummary().contains('Delete Error')) { + deleteErrorFound = true; + break; + } + } + + // Assert that deletions handled correctly + System.assertEquals(false, deleteErrorFound, 'No error should occur for valid deletions.'); + + // Final Verification: Ensure soft credit count is consistent + List finalPscs = [SELECT Contact__c, Opportunity__c, Amount__c, Role_Name__c FROM Partial_Soft_Credit__c]; + System.assertEquals(cPSCExisting, finalPscs.size(), 'Final count should match the original due to errors rolling back changes.'); + } } \ No newline at end of file