Hello dear coders, here is another apex trigger scenario – If the account has related opportunities then we should not able to delete that account. Here is the code for it. Do watch the video for detail understanding.
trigger AccountTrigger2 on Account (before delete) {
if(Trigger.isBefore && Trigger.isDelete) {
List oppr = [SELECT ID, AccountId From Opportunity Where AccountId =: Trigger.old];
if(!oppr.isEmpty() && oppr.size() > 0 ) {
for(Account acc: Trigger.old ) {
acc.addError('You cannot delete this Account it has Opportunity related to it.');
}
}
}
}
In this video we have discussed the trigger in detail.
How this trigger works
This is a before delete trigger on the Account object. Salesforce fires it just before an Account is removed, which is the right moment to stop the delete. Trigger.old holds the Accounts that the user is trying to delete. The trigger queries the Opportunities linked to those Accounts, and if any exist, it calls addError() on the Account. Calling addError() in a before trigger blocks the delete and shows the message to the user.
Step by step
- The user clicks Delete on an Account (or a bulk delete runs).
- The
before deleteevent fires andTrigger.oldcontains the records about to be deleted. - A SOQL query looks for Opportunities whose
AccountIdmatches those Accounts. - If Opportunities are found,
addError()stops the delete and the user sees the custom message. - If no Opportunities are found, the delete goes through normally.
A bulk-safe version
The code above shows the idea. In real projects, a trigger should handle many records at once (for example, a data loader deleting 200 Accounts) and should only block the Accounts that really have Opportunities. This version does both with a single query:
trigger AccountTrigger2 on Account (before delete) {
// Collect the Accounts that have at least one related Opportunity
Set<Id> accountsWithOpps = new Set<Id>();
for (Opportunity opp : [SELECT AccountId FROM Opportunity
WHERE AccountId IN :Trigger.oldMap.keySet()]) {
accountsWithOpps.add(opp.AccountId);
}
// Block only those Accounts
for (Account acc : Trigger.old) {
if (accountsWithOpps.contains(acc.Id)) {
acc.addError('You cannot delete this Account because it has Opportunities related to it.');
}
}
}Test class
Always cover a trigger with tests. This test checks both cases: delete is blocked when an Opportunity exists, and allowed when it does not.
@isTest
private class AccountTrigger2Test {
@isTest
static void blocksDeleteWhenOpportunityExists() {
Account acc = new Account(Name = 'Test Account');
insert acc;
insert new Opportunity(Name = 'Test Opp', AccountId = acc.Id,
StageName = 'Prospecting',
CloseDate = Date.today().addDays(30));
Test.startTest();
Database.DeleteResult result = Database.delete(acc, false);
Test.stopTest();
System.assert(!result.isSuccess(), 'Delete should be blocked');
}
@isTest
static void allowsDeleteWhenNoOpportunity() {
Account acc = new Account(Name = 'Lonely Account');
insert acc;
Test.startTest();
Database.DeleteResult result = Database.delete(acc, false);
Test.stopTest();
System.assert(result.isSuccess(), 'Delete should be allowed');
}
}Interview tips and common questions
- Why before delete and not after delete? In a before trigger the record is still in the database, so
addError()can stop the delete. After delete, it is too late. - Can this be done without code? Yes. A record-triggered Flow that runs before the record is deleted can look up related Opportunities and stop the delete with a Custom Error element. Mention both the trigger and the Flow approach in an interview to show you know the options.
- Why use
Trigger.oldMap.keySet()? It gives the Ids of all records in the batch, so one SOQL query handles up to 200 records and you stay within governor limits. - Why use a custom trigger for this? It gives you full control over the rule and the message shown to the user, and you can change the condition later, for example to block the delete only for open Opportunities.
If you want to see a similar scenario, browse the other Apex trigger scenarios on CodersBugs and try writing the same logic as a Flow to compare.
why video is private
We will make them public soon.