+ Start a Discussion
chaitanya sfdcchaitanya sfdc 

How to write a trigger to prevent record edit based on a field value


I need to write a trigger on base of a field value:

I have three objects:
Call__c -- Master object
Call_Objective__c -- detail object
Call_Asset__c -- detail object

In Call__c I have a picklist field called Status__c which have values like "In Progress", "Not Started" and "Completed".

Now my requirement is, I need to write a trigger that prevents updating of Call__c and creating, updating and deleting of  Call_Objective__c and Call_Asset__c records when Status__c is set to "Completed"

Thanks in Advance
Bhupendra Kshatri 6Bhupendra Kshatri 6
You can write a validation rule on Call__c to prevent user from Updating the Call__c record  when Completed.
ISPICKVAL(Status__c ,'Completed'),
So if any of the Field1, Field2, etc, etc value is changed by a profile except the profile ProfileName(if you need this) it will display an error message.

In trigger you can do the following :
In the for loop for as following collect the Ids of all the call_c into a set.
(Call__c c : trigger.new){
No query for Call_Objective__c, Call_Asset__c which are associated with Call__c and put them in a list.
eg ListOfCallObjectives = [Select Id from Call_Objective__c where Call_lookup__c In : CallObjIdsSet]
similar one for Call_Asset__c .

Now you can delete the collection in List or iterate to update them.

I hope this helps. Do not forget to mark it as an answer if it solves your problem.
srlawr uksrlawr uk
Before I rattle out a big answer on this, can I just quickly ask why your requirement is to do this with a trigger? Seems to me you could achieve simple edit-blocking rules using declarative tools like workflow and validation rules?
srlawr uksrlawr uk
So yep: What Bhupendra said with regards to the validation rule (including an exclusion for certain profiles, very nice!) you can use a similar rule on the child objects - but referencing

ISPICKVAL(Call__r.Status__c, 'Completed')

for the check before blocking edit or creation of the record.. you could also stick a formula field in the child objects with "Parent Status" that references that relationship if you want slick performance etc.etc.
chaitanya sfdcchaitanya sfdc

Hi Bhupendra and srlawr thanks for your replies. I need to prevent the record creation and edit, this can be done through validation rules as suggested by you. But also I need to prevent record deletion if status is Completed.

Call__c should not be updated if Status__c is Completed also related Call_Objective__c & Call_Asset__c  should not get created/Edited/Deleted. So I guess trigger is required to do this. And I don't have much knowledge about trigger. Please help me to get out of this.
srlawr uksrlawr uk
ah bummer, fair shout. There is no way to prevent deletion via validations rules (that I know of) - you might be able to do this using process builder though? Might be worth looking into, to save code/tests/releases/maintenence headaches!!

If not though, what I would do first is create a formula field on the two child records called "Parent_Call_Status__c" with the value:
which will bring in the parent record status automatically (saves us having to query for them in the trigger). The trigger you are looking for will be something like this:
trigger CallObjectiveProtectionTrigger on Call_Objective__c (
    before insert,
    before update,
    before delete
)  {

for(Call_Objective__c thisObjective : trigger.New() {
        thisObjective.addError('I\'m sorry Dave. I can't do that.');

(that is hand typed in this window so it might not be perfect... #syntaxError )

You will want one of those for both Call_Objective and Call_Asset - you won't need one for the parent as you won't be able to delete them because their children will throw the above errors on their behalf.

You will also need some test coverage too (personally I would test inserting, updating and deleting these objects when they do and don't hit the status "Completed" criteria... which is one of those scenarios where the tests will be about 10x the length of the actual code!)

chaitanya sfdcchaitanya sfdc
Hi srlawr,

Thanks for the reply. I tried with your code with few changes. But even if I changed the status as Complete, It's triggered the error. But my requirement is if the prior value of Status is Complete and user tries to modify or delete the detail records it should trigger the error. Here is the code I tried.

trigger CallPlanObjProtectionTrigger on Call_Objective__c (before insert, before update, before delete) 

for(Call_Objective__c CO : trigger.new) {
    if(Trigger.oldMap.get(CO.Id).Call_Status__c == 'Complete')
        CO.addError('Call Status Completed. Should not delete Call Object');


Please help me.
srlawr uksrlawr uk
You seem to be stuck between two different events happening here in the trigger.. and I'm not sure which one you are trying to target.

In a "delete" trigger - the Status cannot/will not be changing - that would be in an update trigger. The only thing that is happening in a Delete trigger is the removal of the child record? Therefore checking oldMap is no different to looking at the value in trigger.new

Also: Take a quick look here at when trigger.new and trigger.oldMap are available: https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex_triggers_context_variables.htm   ...just for interest.

So what are you actually trying to trigger on... the change in status to/from "Completed" - or the deletion of a child record when the parent is deleted, or even the deletion of a child record when a parent status has been completed?

The key thing to remember is the trigger has to fire on one single event on one type of sObject.
chaitanya sfdcchaitanya sfdc
Hi Srlawr,

with this code you suggested,  I am getting error even at the time of changing status to Completed. But I need to throw the error only after Status set to Complete once. i.e., If Status is already Completed and I try to add or delete any call objectives or call assets then it should throw the error. Help will be appreciated :).