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

Preventing the deletion of contact role

So here is the business case:


If an opportunity is ia a mature stage, prevent the deletion of all contact roles.


Here is the code I created to accomplish this goal. I need to stop the deletion from occuring on teh COntact Rols, but cannot write triggers on contact role. The below controller is associated to a VF Page to get it to run taht is hiddien in the standard layout.


public with sharing class CROppHelper {
	public Opportunity opp;
	public CROppHelper( ApexPages.StandardController stdController ) {
        opp = ( Opportunity )stdController.getRecord();        
    public void rollupOppContactRole(){
    @future public static void rollupOppContactRoleFuture( Id oppId ) 
    	Integer priCnt;
        OpportunityContactRole[] oppRoleList = [SELECT ContactId, IsPrimary FROM OpportunityContactRole
                                         WHERE OpportunityId = :oppId];
        Opportunity opp = [Select ID from Opportunity where Id =: oppId];
        If(oppRoleList.size() > 0)
        	for( OpportunityContactRole oppRole : oppRoleList ) 
        		if(oppRole.IsPrimary == TRUE)
        			priCnt = 1;
        		if(priCnt == 1)
        			opp.addError('Opportinuty must have a Primary Contact Role in order to be in this stage.');
        update opp;

VF Page

<apex:page standardController="Opportunity" extensions="CROppHelper" action="{!rollupOppContactRole}"/>



 When a CR is deleted the code is being triggered to run, but it is not preventing the deletion, nor is it displaying the error on the opportunity. Can I have some assistance please?




an alternative design would be to use a Custom apex component associated with a custom controller that would


a) handle any delete requests with appropriate error checking

b) redirect edit requests to the standard, out-of-box SFDC edit OpportunityContactRole (OCR) page

c) return the list of all OCR for a given Contact or Opportunity


Using @future to prevent the DML on the SFDC OCR delete event does not seem workable.  Normally, one prevents deletes either in a before delete trigger (unavailable in this use case) or by providing a 'delete' button that goes to some action on some controller extension/custom controller or, I suppose with Javascript and queries to SFDC.


The custom component  (or custom related list) gives you the ability to associate a controller action with the delete command




How would I associate the component to the delete command? It is a part of the VF page just like my current solution.




Here is a snippet of what I did for an Opportunity related list on an Account VF page where I needed to enforce deletion rules in code


<apex:page standardController="Account" extensions="AccountControllerExtension">

	<apex:pageMessages id="pageMsgs" showDetail="true"/>
	<apex:detail inlineEdit="true" relatedList="false" showChatter="true" subject="{!}"/>

  	<a name="OpportunitiesRL"></a>
	<apex:form id="oppoRelatedList">
		<apex:pageBlock title="Opportunities">
			<apex:pageBlockButtons location="top">
				<apex:commandButton value="New" action="{!URLFOR($Action.Opportunity.New,null,[])}"/>
			<apex:pageBlockTable value="{!opportunities}" var="o">
				<apex:column >
					<apex:facet name="header"><apex:outputText value="Action"/></apex:facet>
       	 			<apex:outputLink title="" value="/{!}/e?retURL=/{!}" style="font-weight: normal; color: rgb(35,111,189); text-decoration: none">Edit
       				<a href="javascript&colon;if (window.confirm('Are you sure?')) deleteOppo('{!o.Id}');" style="font-weight: normal; color: rgb(35,111,189); text-decoration: none">Del</a>
				<apex:column headerValue="Name">
					<apex:outputLink value="{!URLFOR($Action.Opportunity.View,}">{!}</apex:outputLink> 
				<apex:column value="{!o.type}"/>
				<apex:column value="{!o.stageName}"/>
				<apex:column value="{!o.closeDate}"/>
				<apex:column value="{!o.amount}"/>

		<apex:actionFunction action="{!deleteOppo}" name="deleteOppo" reRender="oppoRelatedList, pageMsgs" >		<!--  Must be within apex:form tabs -->
   			<apex:param name="oppoid" value="" assignTo="{!selectedOppoId}"/>

 I used a custom related list via a pageBlockTable and a controller that fetched the Accoount's Opportunities. The Edit and View commands were done with outputLink components; the Delete command went to an actionFunction that invoked a method in the Account's controller where the delete logic and delete validation occurred.


In your case, you would build this on the Opportunity Detail VF page and have the OCR delete command go to your Opportunity controller that would enforce deletion rules and ifOK, do DML to delete the OCR