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
Maria22Maria22 

track stage duration of the opp Stage programatically /customisation features

I got stuck at one place on one of my requirement on which I am working on.

My use case is as follows:
 
SCENARIO 1: Close Opportunity when Upgrade Order is cancelled 
GIVEN a a person account record in Salesforce AND the has an open upgrade opportunity record in Salesforce AND the opportunity record type equals "Upgrade Opportunity" AND the opportunity stage is not "Closed - Won" WHEN the recipient places an upgrade order AND the status of the upgrade order has been changed to Cancelled THEN the open upgrade opportunity Stage field is set to "Closed - Lost" AND all other related opportunities that also meet this scenario criteria (Upgrade Opportunity, not Closed-Won) are set to "Closed - Lost"
 
CENARIO 2: Close Opportunity when Upgrade Order is shipped (ie Closed in Salesforce)
GIVEN a person account record in Salesforce
    AND thehas an opportunity record in Salesforce 
    AND the opportunity record type equals "Upgrade Opportunity"
    AND the stage of the opportunity record is not Closed-Won
    AND the stage of the opportunity record is not Closed-Lost (unless the stage was automatically set to Closed-Lost within the past 90 days due to an order cancellation)
WHEN the places an upgrade order
    AND the upgrade order has a status of Closed
THEN the open upgrade opportunity Stage field is set to "Closed - Won"
    AND all other related opportunities that also meet this scenario criteria (Upgrade Opportunity, not Closed-Won, not Closed-Lost unless automatically changed to Closed-Lost within 90 days) are set to "Closed - Won"

I have done 1st scenario and works well.For 2nd scenario also I have done almost everything except one scenario where I need to track "the stage of the opportunity record is not Closed-Lost (unless the stage was automatically set to Closed-Lost within the past 90 days due to an order cancellation)

Below is my Apex Class which runs in before update from Apex Trigger:
 
public with sharing class OrderTriggerService extends TriggerService {


public void updateOpportunityStage(){
    Set<Id> setIds = new Set<Id>();
    Map<Id,Order> orderOldMap =(Map<Id,Order>)oldMap;
    Set<Opportunity>  updateOppties=new Set<Opportunity>();

    for (Order childObj : (List<Order>) newList) {

        if(childObj.Account_Record_Type_Name__c == 'INDIA' && childObj.Record_Type_Dev_Name__c == 'Upgrade_Order'){ 
        setIds.add(childObj.AccountId);

        }
    }
    if (setIds.isEmpty()) { return; }

 //Set<Opportunity>  updateOppties=new Set<Opportunity>();
  for(Account acc : [select id,Name
                        (selectId,Name,closedate,amount,StageName
                             from opportunities where 
                            Record_Type_Dev_Name__c='Upgrade_Opportunity') 
                                from Account where id in:setIds]){

                         for(Order orders : (List<Order>) newList){            
                                for(Opportunity opps : acc.opportunities){

                                    if(opps.StageName<>'Closed Won' && orders.Status!=orderOldMap.get(orders.Id).Status && orders.Status=='Cancelled'){
                                             opps.StageName='Closed Lost'; 


                                    }

                            else if((opps.StageName<>'Closed Won'||opps.StageName<>'Closed Lost')&& orders.Status!=orderOldMap.get(orders.Id).Status && orders.Status=='Closed'){         
                                        opps.StageName='Closed Won';

                             }

                            updateOppties.add(opps);
                         }
                         }
                     }

                    List<Opportunity> updateopps=new List<Opportunity(updateOppties);
                    update updateopps;

}
  }
I am not able to find a way to track opp.stagename<>'Closed Lost'(unless the stage was automatically set to Closed-Lost within the past 90 days due to an order cancellation) in my class.
Kindly help.

Any help will be greatly appreciated.

Many thanks in advance
 
Best Answer chosen by Maria22
monsterloomismonsterloomis
Hi Maria,

You can track the Stage field on opportunity via the standard history tracking feature (accessible from the fields for that object in setup), and then identify the opportunities that have had history changes before looping through your Trigger.new set. You can then use that set further along in the loop to see if it contains each opportunity id as you go.

One big caveat to this approach is that histories can get large, and you'll want to watch performance on this carefully and test accordingly if you decide to go down a road like this. Here's snippet to get you started. Hope that helps, and good luck!
Set<Id> oppIds = Trigger.newMap.keySet();
Set<Id> closedLostIds = new Set<Id>();
for (Opportunity o:[SELECT Id, (SELECT Id, NewValue FROM Histories WHERE Field = 'StageName' AND CreatedDate = LAST_N_DAYS:90) FROM Opportunity WHERE Id IN :oppIds]) {
    if (o.Histories != NULL) {
        for (OpportunityFieldHistory ofh:o.Histories) {
            if (ofh.NewValue == 'Closed Lost') { // can't filter this in a subquery
                System.debug(ofh.Id);
                closedLostIds.add(o.Id);
                break;// only need to find it once                    
            }
        }
    }
}

 

All Answers

monsterloomismonsterloomis
Hi Maria,

You can track the Stage field on opportunity via the standard history tracking feature (accessible from the fields for that object in setup), and then identify the opportunities that have had history changes before looping through your Trigger.new set. You can then use that set further along in the loop to see if it contains each opportunity id as you go.

One big caveat to this approach is that histories can get large, and you'll want to watch performance on this carefully and test accordingly if you decide to go down a road like this. Here's snippet to get you started. Hope that helps, and good luck!
Set<Id> oppIds = Trigger.newMap.keySet();
Set<Id> closedLostIds = new Set<Id>();
for (Opportunity o:[SELECT Id, (SELECT Id, NewValue FROM Histories WHERE Field = 'StageName' AND CreatedDate = LAST_N_DAYS:90) FROM Opportunity WHERE Id IN :oppIds]) {
    if (o.Histories != NULL) {
        for (OpportunityFieldHistory ofh:o.Histories) {
            if (ofh.NewValue == 'Closed Lost') { // can't filter this in a subquery
                System.debug(ofh.Id);
                closedLostIds.add(o.Id);
                break;// only need to find it once                    
            }
        }
    }
}

 
This was selected as the best answer
Maria22Maria22
Hi Monster
Thanks for your response. I solved the 90days scenario. We are using one field on opportunity which tracks the date when last stage changed on oppty. So I am taking the help of that field to determine something like if that field date is greater than 90days and oppty stage is equals to Closed Lost then change oppty to Closed Won if related orders of the opptys account becomes Cancelled. But now I stuck at somewhere else. I posted my question in developer forum but no one has replied yet. Can you pls help me to look into the same and guide me what's I am doing wrong or tweek the code if possible so that code works as per my req. Below is the link for my question which I posted
https://developer.salesforce.com/forums/ForumsMain?id=9060G0000005pUhQAI

Kindly help me. 

Many thanks in advance 
Maria22Maria22
Thanks monsterloomis for your response. I got the solution to track 90days scenario. 
Actually there is a field on opportunity which wewe use and am succeful on same. But I stuck at some other point. I posted the same in developer community but no one has replied yet. Can you help me to resolve my problem or tweek the code which fulfills my requirement or guide me. The link is as below
https://developer.salesforce.com/forums/ForumsMain?id=9060G0000005pUhQAI

Kindly help me.

Many thanks in advance
monsterloomismonsterloomis
This one can be marked as solved, then? Looks like maybe you found your own solution, so this one is all set.