function readOnly(count){ }
Starting November 20, the site will be set to read-only. On December 4, 2023,
forum discussions will move to the Trailblazer Community.
+ Start a Discussion
aamDevaamDev 

Before Delete Trigger for Contact - Bulk Test Error

I've attempted to create a before delete trigger on the Contact object that will disallow deletion if an Activity (Event or Task) exists for that Contact. If there are no Activities, the Contact will be deleted, along with any associated Production Memberships (custom object). The trigger works on an individual basis using the UI, but I'm not returning the expected amount of rows after deletion in my test class. Unfortunately, I'm too green to know if the error lies in my trigger or my test. Any help would be appreciated. Thanks!

 

Here's my trigger:

 

trigger beforeContactDelete on Contact (before delete) {

	//Create Set of Contacts from trigger
	
	Set<ID> contactIDs = new Set<ID>();
	
	for (Contact c : Trigger.old) {
		
		contactIDs.add(c.ID);

	}

	//All activities tied to a Contact in the trigger
	
	Set<ID> allActivities = new Set<ID>();
	
	for (Event e : [SELECT WhoId FROM Event WHERE WhoId IN : contactIDs]) {
		
		allActivities.add(e.WhoId);
		
	}
	
	for (Task t : [SELECT WhoId FROM Task WHERE WhoId IN : contactIDs]) {
		
		allActivities.add(t.WhoId);
		
	}
	
	//All Contacts with activities
	
	Set<ID> dirtyContacts = new Set<ID>();

	for (Contact c : [SELECT ID FROM Contact WHERE ID IN : allActivities]) {
		
		dirtyContacts.add(c.ID);
			
	}
	
	//Create set of Contacts from trigger without activities
	
	Set<ID> cleanContacts = new Set<ID>();
	
	cleanContacts.addAll(contactIDs);
	cleanContacts.removeAll(dirtyContacts);
	
	//Create List of Production Memberships tied to a clean Contact
	
	List<Production_Member__c> productionMemberships = [SELECT ID FROM Production_Member__c WHERE Contact__c IN : cleanContacts];
	
	//Delete the contact and the Production Member
	
 	for (Contact c : Trigger.old) {
 		
 		if(Trigger.isBefore) {
 		
	 		if(Trigger.isDelete) {
	 			
	 			if(dirtyContacts.containsAll(Trigger.oldMap.keySet())) {
	 				
	 				c.addError('This contact has an activity and therefore cannot be deleted');
	 				
	 			} else if(cleanContacts.containsAll(Trigger.oldMap.keySet())) {
	 				
	 				if(productionMemberships != null) {
	 					
	 					delete productionMemberships;
	 					
	 				}
	 				
	 			}

	 		}
 		
 		}
 		
 	}

}

 

Here's the test class:

 

@isTest
private class testBeforeContactDelete {

    static testMethod void bulkContactDeleteTest() {

		//Create the list of contacts to test delete
		
		List<Contact> testContacts = [SELECT ID FROM Contact WHERE Account.Name = 'contactTest'];
		
		//Start Contact Delete Trigger
		
		Test.startTest();
		
		delete testContacts;
		
		Test.stopTest();
		
		//Check for desired results
		
		List<Contact> deletedContacts = [SELECT ID FROM Contact WHERE Account.Name = 'contactTest'];

		List<Production_Member__c> deletedPMs = [SELECT ID FROM Production_Member__c WHERE Contact__r.Account.Name = 'contactTest'];

    }
}

 

Best Answer chosen by Admin (Salesforce Developers) 
SteveBowerSteveBower

 

	// Get the activities tied to the Contacts
	Set<ID> allActivities = new Set<ID>();

        for (Event e: [SELECT WhoId FROM Event WHERE WhoId IN :Trigger.oldMap.keySet()]) allActivities.add(e.WhoId);
        for (Task t: [SELECT WhoId FROM Task WHERE WhoId IN :Trigger.oldMap.keySet()]) allActivities.add(t.WhoId);

        Set<Id> okToKill = new Set<Id>();
        for (Contact c: Trigger.old) {
              if (allActivities.contains(c.id)) {
                   c.addError('This contact has an activity and therefore cannot be deleted');
              } else {
                   okToKill.add(c.id);
              }
        }
	
	//Create List of Production Memberships tied to Contacts with no activity and delete them.
	List<Production_Member__c> productionMemberships = [SELECT ID FROM Production_Member__c WHERE Contact__c IN :okToKill];
        delete productionMemberships;

}

 

 

I'd probably do something like the above.    Note I'm just typing this into the editor, I'm sure it's not perfect.  :-)

 

The theory behind the test classes is that they are data independent from the current state of an instance.  So, somewhere in your test script I'd have expected to see code which did the following:

 

1. creates 5 Contacts

2. creates 1 task for Contact 1

3. creates 1 event for Contact 2

4. creates both a task and an event for Contact 3

5. creates production memberships for Contacts 4 and 5

6. creates production memberships for Contact 1 and 2

starttest

7. does a delete of all the contacts 

stoptest

8. uses 3 system asserts to check that contact 1, 2 and 3 are still present.

system.assertEquals(1,[select count() from contact where id = :con[0].id);   // example for one contact

9. check that contact 4 is gone.

10 check that production memberships that were linked to 4 and 5 are gone.

11 check that production memberships that were linked to 1and 2 are still present.

 

I hope this helps, best, Steve.

 

All Answers

SteveBowerSteveBower

 

	// Get the activities tied to the Contacts
	Set<ID> allActivities = new Set<ID>();

        for (Event e: [SELECT WhoId FROM Event WHERE WhoId IN :Trigger.oldMap.keySet()]) allActivities.add(e.WhoId);
        for (Task t: [SELECT WhoId FROM Task WHERE WhoId IN :Trigger.oldMap.keySet()]) allActivities.add(t.WhoId);

        Set<Id> okToKill = new Set<Id>();
        for (Contact c: Trigger.old) {
              if (allActivities.contains(c.id)) {
                   c.addError('This contact has an activity and therefore cannot be deleted');
              } else {
                   okToKill.add(c.id);
              }
        }
	
	//Create List of Production Memberships tied to Contacts with no activity and delete them.
	List<Production_Member__c> productionMemberships = [SELECT ID FROM Production_Member__c WHERE Contact__c IN :okToKill];
        delete productionMemberships;

}

 

 

I'd probably do something like the above.    Note I'm just typing this into the editor, I'm sure it's not perfect.  :-)

 

The theory behind the test classes is that they are data independent from the current state of an instance.  So, somewhere in your test script I'd have expected to see code which did the following:

 

1. creates 5 Contacts

2. creates 1 task for Contact 1

3. creates 1 event for Contact 2

4. creates both a task and an event for Contact 3

5. creates production memberships for Contacts 4 and 5

6. creates production memberships for Contact 1 and 2

starttest

7. does a delete of all the contacts 

stoptest

8. uses 3 system asserts to check that contact 1, 2 and 3 are still present.

system.assertEquals(1,[select count() from contact where id = :con[0].id);   // example for one contact

9. check that contact 4 is gone.

10 check that production memberships that were linked to 4 and 5 are gone.

11 check that production memberships that were linked to 1and 2 are still present.

 

I hope this helps, best, Steve.

 

This was selected as the best answer
aamDevaamDev

Hi Steve,

 

Thanks for your reply. I appreciate your test class explanation - I'll see what I can put together. As for your trigger recommendations, is there anything else that needs to be written to delete the appropriate contacts after adding the Contact IDs to the okToKill Set? I apologize if I'm assuming too much, I know you made your edits on the fly, but I remain confused as to where in the loop the Contact is being deleted.

 

Thanks for your continued help.

aamDevaamDev

Never mind, I figured it out. Thanks agian.