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
Kartik ChadhaKartik Chadha 

Prevent Contact Deletion

I am a newbie to salesforce and i have a requirement to which i need suggestions how to approach this requirement
I have 4 contacts related to one account and when someone deletes contacts, he should not be able to delete the last contact related to the account.For example: in account A1 i have 4 contacts and someone deletes the 3 contacts from that account then it should be deleted, after that there will be only 1 contact related to that account and den someone tries to delete the last contact than it should not be deleted and throws an error "There should be atleast one contact for this account <<account name>>".
How can i achieve this using trigger?
Here I want to use List rather than Map.
David @ ConfigeroDavid @ Configero
How about a rollup on the Account showing the number of Contacts, let's call this filed Account.Contact_Count__c.

Check the Account.Contact_Count__c in a before delete trigger, calculate the number of contacts related to each Account that are attempting to be deleted, and make sure deleting this number won't reduce the Contact count to 0.  I would suggest using a Map<Id, List<Contact>> which will store a key of the Account Id, and a value which is a list of Contacts associated with that Account Id.

If you cannot or do not want to use a rollup field, then you will have to run a SOQL query for each delete transaction instead of referencing the rollup field.

You will also have to make a decision if you are going to "choose" which is the last Contact to error if they attempt to delete multiple Contacts for an Account in one transaction, or just error all Contacts for that Account and force the user to choose manually (safer to let the User choose versus guessing for them, in my opinion)

When you have the trigger all said in done, you will want to call .addError('There should be at least one contact for this Account') on the Contact record.

Reference the following resources for more information:
https://trailhead.salesforce.com/en/modules/point_click_business_logic/units/roll_up_summary_fields
https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex_triggers_context_variables.htm

https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex_methods_system_sobject.htm
https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex_triggers_order_of_execution.htm
NagendraNagendra (Salesforce Developers) 
Hi Kartik,

In your trigger, run a query for all contacts related to the account. If you are trying to delete all of them in this trigger, don't allow it. I don't know how you want to deal with people deleting multiple contacts at the same time, but let's say you will simply disallow the entire delete and the user has to retry with fewer contacts. If you want to come up with some logic that deletes all but 1 of the contacts, that's up to you. Something like:
Trigger OnContactDelete on Contact (before delete) {
   Set<ID> accountIds = new Set<ID>(); //all accounts that contacts are being deleted from
   for (Contact contact : Trigger.old) {
       accountIds.add(contact.AccountId);
   }

   List<Contact> contacts = [SELECT Id, AccountId FROM Contact WHERE AccountId IN :accountIds]; //get all of the contacts for all of the affected accounts

   Map<ID, Set<ID>> deleteMap = new Map<ID, Set<ID>>(); //map an account ID to a set of contact IDs being deleted
   Map<ID, Set<ID>> foundMap = new Map<ID, Set<ID>>(); //map an account ID to a set of contact IDs that were found by the query

   for (Contact deleteContact : Trigger.old) {
     Set<ID> idSet = deleteMap.get(deleteContact.AccountId);
     if (idSet == null) {
       idSet = new Set<ID>();
     }

     idSet.add(deleteContact.Id);
     deleteMap.put(deleteContact.AccountId, idSet);
   }

   for (Contact foundContact : contacts) {
     Set<ID> idSet = foundMap.get(foundContact.AccountId);
     if (idSet == null) {
       idSet = new Set<ID>();
     }

     idSet.add(foundContact.Id);
     foundMap.put(foundContact.AccountId, idSet);
   }

   for (ID accountId : accountIds) { //go through each affected account
     Set<ID> deleteIds = deleteMap.get(accountId);
     Set<ID> foundIds = foundMap.get(accountId);

     if (deleteIds != null && foundIds != null && deleteIds.containsAll(foundIds)) {
       for (Contact contact : Trigger.old) {
         if (deleteIds.contains(contact.Id)) { //this is one of the contacts being deleted
           contact.addError('This contact is potentially the last contact for its account and cannot be deleted');
         }
       }
     }
   }
 }

Note, I just typed this up in SO and haven't actually tested the code at all, even for syntax errors like missing semicolons or braces. It should work in theory, but there may be better ways of doing it.

Hope this helps.

Kindly mark this as solved if the information was helpful.

Thanks,
Nagendra
 
Sumit Kumar Singh 9Sumit Kumar Singh 9
You can try this one too - 
trigger PreventLastContactDeletion on Contact (before delete) {
    
    Map<Id, List<Contact>> AccountAndContactMap = new Map<Id, List<Contact>>();
    List<Contact> tempConList = new List<Contact>();

    Set<Id> accountIds = new Set<Id>();
    for(Contact c : trigger.old) {
        accountIds.add(c.accountId);
    }
    
    for(Contact con : [SELECT id, Account.name FROM Contact WHERE accountId IN : accountIds])  { 
        if(AccountAndContactMap.keyset().contains(con.accountId))  {
            tempConList = AccountAndContactMap.get(con.AccountId); 
        } else {
            tempConList = new List<Contact>();
        }
        tempConList.add(con);     
        AccountAndContactMap.put(con.accountId, tempConList);             
    } 
    
    for(Contact con : Trigger.old) {
        if(AccountAndContactMap.get(con.accountId).size() == 1) {
            con.addError('There should be atleast one contact for this account');       
        }
    }
}