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
Iceman123Iceman123 

How to obtain the Id of a SObject in a test case before INSERT

Hi there,

 

I have a trigger which works fine in Sandbox, but cannot be deployed to production because there doesn't seem to be a way to obtain the Id of a SObject before the actual Insert, kind of a chicken and the egg thing.  Here is what I have:

 

Trigger:

 

    Opportunity[] opps = Trigger.new;

        for (Opportunity o:opps)
        {
            OpportunityContactRole[] ocrs = [Select Id from OpportunityContactRole where OpportunityId=:o.Id];
            if (ocrs.size() == 0)
            {
                o.addError('A contact role does not exist.');
            }
        }

 

The above trigger prevents a save on an Opportunity if there's no OpportunityContactRole associated to the Opportunity.  It works like a charm.

 

The problem is that in order to test this trigger class, I need the ability to create an OpportunityContactRole before I can save the test Opportunity (otherwise, the system will not allow the test Opportunity to be saved because there's no OpportunityContactRole).  Here are the challenges:

 

- In order to create an OpportunityContactRole test object, I have to associate it to an Opportunity object via the OpportunityId field (or establish a relationship between this OpportunityContactRole record and the soon to be created Opportunity record), but this ID is not generated until I call insert on the test Opportunity object. 

- I cannot create the Opportunity first because the insert will fail for there's no OpportunityContactRole associated to the Opportunity.

- We cannot set a custom ID for an Opportunity object (not allowed to set Ids on any Sobjects).

 

The only thing I can think of is if there's a way to obtain a valid ID for an opportunity object before the insert call and use that to create the OpportunityContactRole first, and then call insert on the opportunity object.  But I don't think this can be done ....  How would you approach this problem?

 

thanks in advance for the feedback.

Pi

 

 

Best Answer chosen by Admin (Salesforce Developers) 
Iceman123Iceman123
Thanks for the suggestion and sample.  We ended up implementing a trigger that sets a customer field

Has_Contact__c (Yes when the OpportunityContactRole's size is > 0, and No, if not) against the Opportunity and also created a validation rule against Has_Contact__c (for when it is = No).  This successfully prevents an Opportunity from being created if it does not have an OpportunityContactRole (ie. it prevents users from creating a New opportunity from anywhere in the system unless they create it from a specific Contact page).

 

~pi

All Answers

SuperfellSuperfell
But don't you have the exact same problem outside of tests, seems like this trigger is going stop you being able to create any opportunities at all.
Iceman123Iceman123

Actually, maybe it helps to specify our use case.  Here is the problem we are trying to tackle:

- If someone creates an Opportunity directly from the Opportunity tab using the New button, SF does not add a contact role to the Opportunity.  So the trigger specified above would correctly catch this condition and prevent users from saving a record.

- If someone creates an Opportunity from an actual Contact page (using the New opportunity button there), an OpportunityContactRole record is created by SF and associated to the Opportunity object, so the trigger will not prevent them from saving.

 

In other words, the trigger is created so that it will allow folks to save an Opportunity only if they are creating it from a specific Contact page.  It prevents users from creating opportunities from the Opportunity tab page itself (which does not create the OpportunityContactRole association).

 

Does this make sense?

 

thanks,

Pi

MiguelGuerreiroMiguelGuerreiro

Hi,

 

Can't you just create a validation rule on Opps for that?

 

Miguel

Iceman123Iceman123

Maybe I am missing something obvious :( how do you get to the OpportunityContactRole object from an Opportunity validation rule?  Essentially, I would like the validation to be performed against the OpportunityContactRole records associated to each opportunity.

 

CaptainObviousCaptainObvious

I had a similar requirement where I had to restrict the creation of a Target to a Contract record. Instead of a trigger, I created a Visualforce page to override the New Target button. The page pre-populates with information carried over from the contract:

<apex:page standardController="Target__c" showHeader="true" sidebar="true" tabStyle="Target__c" extensions="targetNewExt"> <style> .aField { width: 200px; } .nField { width: 380px; height:80px; } .cpsHeader { line-height: 1.5em; font-weight: bold; padding-left:0.3em; } </style> <script type="text/javascript"> function populate() { var error = ''; error += document.getElementById("{!$Component.myForm.pgB.tError}").innerHTML if ( error=='' ) { document.getElementById("{!$Component.myForm.pgB.pgBsec0.tAccount}").value = '{!contract.account.name}'; document.getElementById("{!$Component.myForm.pgB.pgBsec0.tContract}").value = '{!contract.contractnumber}'; document.getElementById("{!$Component.myForm.pgB.pgBsec0.tValid}").checked = true; document.getElementById("{!$Component.myForm.pgB.pgBsec0.tIDate}").value = '{!MONTH(contract.startDate)}/{!DAY(contract.startDate)}/{!YEAR(contract.startDate)}'; document.getElementById("{!$Component.myForm.pgB.pgBsec0.tFDate}").value = '{!MONTH(contract.endDate)}/{!DAY(contract.endDate)}/{!YEAR(contract.endDate)}'; } } window.onload=populate; </script> <apex:sectionHeader title="Target Edit" subtitle="New Target" /> <apex:form id="myForm"> <apex:pageBlock title="Target Edit" mode="edit" id="pgB"> <apex:pageMessages id="tError" ></apex:pageMessages> <apex:pageBlockButtons > <apex:commandButton value="Save" action="{!save}" rendered="{!validEntry}"/> <apex:commandButton value="Cancel" action="{!cancel}"/> </apex:pageBlockButtons> <apex:pageBlockSection title="Information" columns="2" id="pgBSec0" rendered="{!validEntry}"> <apex:inputField id="tAccount" value="{!Target__c.Account__c}" required="true" /> <apex:inputField id="tValid" value="{!Target__c.Valid__c}" /> . . . </apex:page>

The controller extension:

 

public class targetNewExt { String cid; Target__c nTarget; Contract contract; public targetNewExt(ApexPages.StandardController stdController) { nTarget = (Target__c)stdController.getRecord(); this.cid = ApexPages.currentPage().getParameters().get('cid'); if (cid == null) { ApexPages.addmessage(new ApexPages.message(ApexPages.severity.Error,'Please click the New button from a Contract record')); } } public boolean getvalidEntry(){ Boolean x; if(cid == null) { x = false; } else { x = true; } return x; } public Contract getContract() { if (cid!=null) { contract = [SELECT Id, AccountId, ContractNumber, Account.Name, StartDate, EndDate FROM Contract WHERE id = :cid]; return contract; } else { return null; } } . . . }

Next, I created a custom list button on the Target object with the URL:

 

/apex/targetNew?cid={!Contract.Id}

On the Contract page layout, I removed the standard "New" button on the Target related list, and replaced it with my custom button.

Here's what happens:

  • When the user clicks New from the Targets tab, the cid parameter is null. An error message appears, and the only option is to cancel (takes the user back to the Targets Tab).
  • When the user clicks New from the Contract-Target related list, the custom button passes the cid parameter to the visualforce page and the page renders normally.

It may be a little more work, but it gets the job done :smileywink:

Message Edited by CaptainObvious on 09-29-2009 04:23 PM
Iceman123Iceman123
Thanks for the suggestion and sample.  We ended up implementing a trigger that sets a customer field

Has_Contact__c (Yes when the OpportunityContactRole's size is > 0, and No, if not) against the Opportunity and also created a validation rule against Has_Contact__c (for when it is = No).  This successfully prevents an Opportunity from being created if it does not have an OpportunityContactRole (ie. it prevents users from creating a New opportunity from anywhere in the system unless they create it from a specific Contact page).

 

~pi

This was selected as the best answer