Spr09 generic force.jpg

With Spring ‘09, Workflow rules, Validation rules and Apex triggers on Opportunities and Opportunity Products (parent) will fire when an update to an Opportunity Product or Schedule (child) causes an update to the parent record. This means your custom application logic on the parent record will always be enforced, ensuring higher data quality and compliance in your Force.com app.

The goal for the New Opportunity Save Behavior feature for the Spring ’09 release is to change the save behavior for Opportunities so that all standard and custom save logic is consistently fired, aligning the behavior across the Force.com Platform.

On Thursday, January 8th at 10am PST, we presented a webinar on this upcoming change and the important steps to prepare your impacted Force.com app. You can watch the recording here.

Is your Force.com app affected?

YES - Your Force.com app is most likely affected if it contains at least ONE of the following customizations:

  • Opportunity and Opportunity Product Validation rules
  • Opportunity and Opportunity Product Universally required fields
  • Opportunity and Opportunity Product Unique fields
  • Opportunity and Opportunity Product Triggers
  • Opportunity and Opportunity Product Workflow rules
  • Triggers on any object that cause an update to the Opportunity and/or Opportunity Product

Important - If your Force.com app does contain one of the above and is not packaged in the correct way, it may be BLOCKED on install in a Spring '09 org. Read below to learn how to create a package that can work in both new and old Opportunity Save Behavior.


Consider a situation in which you have the following Apex Trigger rule on the Opportunity, which updates the Stage field to "Qualification" if the Opportunity amount is greater than $10,000:

trigger OppUdate on Opportunity(before insert, before update) {

for(Opportunity opp : Trigger.new){
        if (opp.Amount >10000) {
        opp.StageName = 'Qualification ';

If the Spring ’09 New Opportunity Save Behavior is inactive, Salesforce runs this Apex Trigger when you edit an opportunity but NOT when you edit the opportunity product or schedule. As a result, if the amount of the opportunity changes because of an update on the opportunity product, the parent opportunity could be in an invalid stage.

If the Spring ’09 New Opportunity Save Behavior is activated, Salesforce runs this Apex Trigger both when you edit an opportunity and when you edit the opportunity product if the changes to the opportunity product affect the associated opportunity's amount or quantity. This ensures the opportunity stage is always updated when the amount is updated by either the opportunity or opportunity product.

How to Test?

So how can we test the above example against new and old Opportunity Save Behavior? You can begin testing in Winter '09 org by adding at least ONE custom Roll-Up Summary field (RSF) on the Opportunity. Adding this custom RSF will emulate the New Opportunity Save Behavior in your Winter ’09 org. To create a custom RSF on the Opportunity, follow these steps:

  1. Go to Setup | App Setup | Customize | Opportunities | Fields
  2. Select New near Opportunity Custom Fields & Relationships
  3. Select Roll-Up Summary as your Data Type and follow the 5-Step Wizard
  4. Upon completion, your org will operate with New Opportunity Save Behavior active

When Spring '09 is released, a custom RSF on the Opportunity is not required. Instead, you will be able to use the Critical Update Console (Figure 1) to activate/deactivate the New Opportunity Save Behavior. Review the Spring '09 Release Notes for steps to activate/deactivate.

Critical Update Console

Figure 1: New Critical Update Console available only in Spring '09

Important - Activation of this update impacts Opportunity and Opportunity Product records in the user interface, API, and desktop clients.

What BLOCKS a Package?

Because this update may cause Salesforce to run your custom application logic in situations where it did not run before, Salesforce may block a customer organization from downloading a Force.com package from the AppExchange to prevent data corruption and application error.

Packages will be designated as available for new save behavior, old save behavior, or both. Package developers can create a package that will work in both new and old save behavior in a customer organizations. The package will be blocked from installation into a customer organization if the following are all true:

  • The package contains any of the following customizations:
    • Opportunity and opportunity product universally required fields
    • Opportunity and opportunity product unique fields
    • Opportunity and opportunity product triggers
    • Opportunity and opportunity product workflow rules (email/tasks/field updates)
  • The package was built on a save behavior that is different from your customer's org setting
  • The package developer has not confirmed that the package will function properly in both Old or New Opportunity Save Behavior

One Package for both Opportunity Save Behaviors

Before you create a new Package that runs in both Opportunity Save Behaviors, it's important to update your Apex Triggers, so they can run successfully in both environments. We recommend 2 options to implement to make sure you Force.com apps runs smoothly.

Option 1 - Implement Opportunity Product / Schedule logic to ensure the Opportunity is successfully updated in the old Opportunity Save Behavior. This will support partner applications that are impacted by Opportunity Product/Schedule updates. The following code is an example to always fire the Opportunity Apex Trigger from above:

trigger OpptyLineItemTrigger on OpportunityLineItem(after insert, after update, after delete){
Set<ID> opptyids=new Set<ID>();

if(Trigger.isInsert || Trigger.isUpdate){
for(opportunitylineitem oli :trigger.new){
for(opportunitylineitem oli :trigger.old){

List<Opportunity> opptys = [select id from Opportunity where id in :opptyids];

Database.saveresult[] sr = Database.update(opptys,false);

With this Apex Trigger on the Opportunity Product, you can ensure the Opportunity will update regardless of new and old Opportunity Save Behavior. The above is an example, you will need to adjust to fit your specific Force.com app. Implementing a solution like this will allow you to create a package that can work in both new and old orgs.

Option 2 - Implement Option 1 as well as a static variable to determine Opportunity save logic, so that a rule/trigger is never fired more than once. This supports partner applications that are agnostic to Opportunity Product/Schedule updates. This is recommended for Force.com apps that may trigger an workflow alert or email to fire. With this option implement, you can be sure your rule/trigger will only fire once. There are two pieces to this. First there is a the Apex Class OSRUtil to make sure the trigger is fired only once:

public class OSRUtil {

private static boolean run = true;

 public static boolean runOnce(){
 return true;
 else{return run;}

 private static testmethod void testOSRUtil(){
 System.assert(OSRUtil.runOnce(), 'Recursion check failed. Please review OSRUtil recursion logic');
 System.assert(!OSRUtil.runOnce(), 'Recursion check failed. Please review OSRUtil recursion logic');

This class contains test methods to ensure adequate code coverage during packaging. Once this class is created, you will want to wrap you parent trigger (Opportunity / Opportunity Product) with a RunOnce Conditional Block:

if(OSRUtil.runOnce())    {

// Insert Partner Code   


So our Opportunity Apex trigger from above would look as follows:

trigger OppUdate on Opportunity(before insert, before update) {

        for(Opportunity opp : Trigger.new){
                    if (opp.Amount >10000) {
                        opp.StageName = 'Qualification ';

With both of these Options implemented, you are ready to create your new package to prevent blocking. The steps are similar to packaging today with one addition:

  1. Build a package that can work in both NEW and OLD Save Behavior orgs.
  2. Upload your new package using the usual upload process
  3. Upon upload, if your package contains customizations that could be affected by New vs. Old Save behavior you will notice that a new Feature titled “Old Opportunity Save Behavior” (Figure 2) is visible in the Package Requirements. (Note: If the Package is created in a new Spring '09 Org, the perm will be called "New Opportunity Save Behavior" - you will still want to uncheck it if you wish to support both behaviors)
  4. Un-checking this option confirms that this package is safe to download into customer orgs that have either Old or New Opportunity Save Behavior activated.


Figure 2: Un-checking Old Opportunity Save Behavior will allow you to upload a package that works for both Save Behaviors

Timeline and Rollout

Spring '09 Release

  • All new orgs and existing orgs NOT impacted will have New Opportunity Save Behavior auto activated

8 weeks after Spring '09 Release

  • All existing orgs NOT impacted will be auto activated
  • All impacted orgs will have the ability to activate New Opportunity Save Behavior

Winter '10 Release

  • All remaining (impacted or not) orgs will be auto activated to New Opportunity Save Behavior


Webinar Q&A

Q: How can we check existing code – is there an impact analysis tool for the current version?
A: There will be an impact analysis tool for Spring '09 that can help you understand the repercussion when activating New Opportunity Save Behavior. For Winter '09, you will need to run the test above.

Q: This presentation is focused on only the opportunity save change, but are ALL metadata changes in winter 09 available? I would like to ensure that informatica integration mappings are updated to acknowledge any deletions
A: Great question. All ISV Partners were invited to participate in our Spring '09 Pre-release Program about 4 weeks ago. If your company missed that email or didnt receive it, please log a request at www.appexchange.com/support. The program allow partner access to the new environment several weeks before Spring '09 is released to customers.

Q: Is this slide deck available to attendees?
A: Yes, the recorded webinar and presentation are available in the above Resources section.

Q: Can the OSRUtil supporting code be used on customized objects other than the Opportunity object to ensure triggers/alerts are only sent once?
A: Yes, the OSRUtil is generic enough to be used on any Standard and Custom Object.

Q: Does the custom RSF need to be on the page layout for the new functionality to work?
A: No, you only need to create the field. It does not have to be on the page layout to activate New Opportunity Save Behavior.

Q: Our application uses the Opportunity object but does not use the Product or Solution objects...would we still be affected?
A: No, since you are not using any of the child objects (Opportunity Products and Schedule), you will not be affected.