1

Currently we have Global care Opportunity for renewal is auto generated 120 days before the End date expiration date. It should be changed such that the new Opportunity is created immediately after the closure of current opportunity. For this we have one Batch class. Please find the below Batch class. Current Requirement is: They don’t want to renewal auto generated before 120 days. Now they want to Creating new renewal opportunity after one year.

7
  • Which of the 3 RecordTypes (if any) is actually the GC Renewal Record Type? Is it GEIP_Renewal, GEIP_SP_OEM, or GEIP_SPOEM_Renewal. Those are the 3 Record Type Names that your batch class is currently looking for. You need to find out what the Developer Name is for that Record Type. If none, then you need to remove these references and replace them with the correct one.
    – crmprogdev
    Jan 30, 2017 at 14:10
  • @crmprogdev, Thanks for your reply..We only considered "GEIP_Renewal" only not any other. So hear we need to consider only for this recTypeId = [Select id,name from RecordType where SobjectType = 'Opportunity' and DeveloperName = 'GEIP_Renewal'][0].Id; not any other. Can you please let me know how i can modify the code based above requirement. Thanks Jan 30, 2017 at 14:24
  • You say "It should be changed such that the new Opportunity is created immediately" but then say "They don’t want to renewal auto generated before 120 days" - Which is it immediately or 120days before?
    – Eric
    Jan 30, 2017 at 14:35
  • @Eric, Yes,They don't want to create new opportunity before 120 days,according to existing logic which i have mentioned above .But they want to create new opportunity after one year(365 days) of END date on the service contract object. Please help us Thanks Jan 30, 2017 at 14:40
  • @waltersfdc Do you want to change all of the other record types that currently exist to the GEIP_Renewal and create new opportunities for them using that Record Type or do you only want to renew that Record Type?
    – crmprogdev
    Jan 30, 2017 at 15:12

1 Answer 1

2

Only your execute method needs to be revised. Here's the essence of what you'll need to change.

global void execute(Database.BatchableContext BC, List <sObject> batch)
{ 
        //Query the id of 'Renewal' record type of opportunity object
        recTypeId = [Select id,name from RecordType where SobjectType = 'Opportunity' and DeveloperName = 'GEIP_Renewal'][0].Id;
        SPOEMRecTypeId = [Select id,name from RecordType where SobjectType = 'Opportunity' and DeveloperName = 'GEIP_SP_OEM'][0].Id;
        SPOEMRenRecTypeId = [Select id,name from RecordType where SobjectType = 'Opportunity' and DeveloperName = 'GEIP_SPOEM_Renewal'][0].Id;
        //For each service contract, create a Renewal opportunity
        System.debug('*** In batch apex class - execute');
        List<ID> scAccountId = new List<ID>();
        for (Sobject obj_acc : batch)
        {
             ServiceContract scon = (ServiceContract)obj_acc;
             scAccountId.add(scon.AccountId);
             scIdList.add(scon.Id);
        }
        List<account>  SCacclist=[select id,name,CSN__c,Master_Account__c from account where id in :scAccountId];
        Map<Id,String> accountname = new Map<Id,String>();
        Map<Id,String> csnname = new Map<Id,String>();
        for(account acc: SCacclist)
        {
           if(acc.Master_Account__c == null) accountname.put(acc.id,acc.name);
           csnname.put(acc.id,acc.CSN__c);
        }
        List<Asset> assetList = [Select id,accountid,Service_Contract__c from Asset where Service_Contract__c in :scIdList];
        if(assetList !=null && assetList.size() > 0)
        {
            for(Asset a: assetList)
            {
               List<Id> templist =scAssetMap.get(a.Service_Contract__c);                
               if(templist==null)
               {
                  templist = new List<Id>();
                  scAssetMap.put(a.Service_Contract__c,templist);
               }
               templist.add(a.Id);
            }
        }
        System.debug('***accountname'+accountname);
        for (Sobject obj : batch)
        {
             ServiceContract sc = (ServiceContract)obj;
             scList.add(sc);
             accName=accountname.get(sc.AccountId);
             accCSN=csnname.get(sc.AccountId);

Here's where your initial code changes will need to start. If I understand your problem correctly, you'll want to combine all of the records from the 3 record types and assign them the single record type as you create new opportunities for them at renewal time.

             if(accName!=null)
             {               
                /*@@ Here's where initial logic changes begin @@*/

                 Opportunity opp = new Opportunity();
                 String str='GlobalCare Renewal-';
                 if(sc.Type__c == 'Solution Provider Contract' || sc.Type__c == 'OEM Contract' || sc.Type__c == 'Solution Provider Contract')
                {
                    opp.RecordTypeId = recTypeId ;
                    opp.QMI_Comment__c = 'GlobalCare Expiring';
                    opp.Service_Contract__c = sc.id;
                    sc.Type__c = 'Solution Provider Contract' // changes all RT's to 'Solution Provider Contract'
                }
                /*@@ initial logic changes end @@*/


                 System.debug('scenddate***'+sc.EndDate);
                 if(sc.Enddate != null)
                {
                 enddate=String.valueOf(sc.Enddate);
                 enddate=enddate.replace('-','/');
                 opp.name = str+enddate+'-'+accName+'-'+accCSN;
                 System.debug('oppname$$$'+opp.name);
                 }else
                 {
                  opp.name = str+accName+'-'+accCSN;
                 }
                 if(opp.name!=null && opp.name.length()>120)
                     opp.name = opp.name.substring(0,119);

                // opp.name ='Renewal Opportunity';
                 opp.AccountId = sc.AccountId;
                 opp.StageName = 'S1-Prospecting';
                 //Date d = Date.valueOf('2099-01-01'); 
                 Date d=sc.Enddate;       
                 opp.CloseDate = d;        
                 opp.Amount = 1; 
                 opp.ForecastCategoryName='Omitted';
                 opp.Business_Area_of_Interest__c='SW';
                 opp.Is_Risk_Review_Required__c='I Don\'t Know';
                 opp.Product_Area_of_Interest__c='Global Care';
                 newOppList.add(opp);
             }
        }
        System.debug('***newOppList'+newOppList);
        if(newOppList != null && newOppList.size()>0)
                 insert newOppList;
         for(ServiceContract sc : scList)
         {
             sc.Processed_for_Renewal__c = true;
         }
         //Check the 'Processed for Renewal' flag for all the processed service contracts
         if(scList != null && scList.size()>0)
             update scList;

         //Create a map for storing the opportunity and whether the opportunity's account is a multisite account
         Map<Id,Boolean> oppMultiSiteAccMap = new Map<Id,Boolean>();
         List<Id> accList = new List<Id>();
         List<Id> allAccList = new List<Id>();
         Map<Id,Id> accMap = new Map<Id,Id>();
         Map<Id,List<Id>> oppAccMap = new Map<Id,List<Id>>();
         Map<Id,List<Id>> accAssetMap = new Map<Id,List<Id>>();
         for(Opportunity opp : newOppList)
         {
             accList.add(opp.AccountId);
             accMap.put(opp.AccountId,opp.Id);
         }
         System.debug('***accMap'+accMap);
         for(Account acc : [Select Multisite_Renewal__c from Account where id =:accList])
         {
             oppMultiSiteAccMap.put(accMap.get(acc.id),acc.Multisite_Renewal__c);
         }
         System.debug('***oppMultiSiteAccMap'+oppMultiSiteAccMap);
         accList.clear();
         List<opportunity> toBeUpdatedList = new List<Opportunity>();
         //Create Opportunity Assets for each Opportunity created
         System.debug('newOppList  '+newOppList);
         for(Opportunity opp : newOppList)
         {
             //Add end user account of opportunity to account list
             accList.add(opp.AccountId);
             //If the end user account of the opportunity is a multisite account, add all the child accounts to the account list
             System.debug('***oppMultiSiteAccMap'+oppMultiSiteAccMap);
             System.debug('***oppMultiSiteAccMap.get(opp.Id)'+oppMultiSiteAccMap.get(opp.Id));
             System.debug('***opp.Id'+opp.Id);
             if(oppMultiSiteAccMap!=null && !oppMultiSiteAccMap.isEmpty() && oppMultiSiteAccMap.get(opp.Id)!=null)
             {
                 List<Id> childAccounts = AccountMultisiteRenewalTriggerHandler.getChildAccounts(opp.AccountId);
                 accList.addAll(childAccounts);
             }
             System.debug('accList   '+accList);
             Decimal spOEMExpectedAmount=0.0;
             Decimal gcExpectedAmount = 0.0;
             //Add the assets of the accounts in the account list to the opportunity
             List<Asset> assetsList = new List<Asset> ();
             assetsList = [Select id,ParentId,Service_Contract__c,type__c,GlobalCare_Level__c,Asset_Value__c,smart_part_number__c from Asset where AccountId in :accList and GlobalCare_Level__c!='None' and GlobalCare_Level__c!='' and Type_of_Key__c != 'Demo' and Status = 'Active'];
             set<id> childAssets = new set<id>();
             set<id> RootAssets = new set<id>();
             Map<id,id> assetHierarchy = new Map<id,id>();
             for(Asset recAsset:assetsList){
                if(recAsset.ParentId!=null){
                    assetHierarchy.put(recAsset.id,recAsset.parentId);
                    childAssets.add(recAsset.id);
                }   
                else
                    RootAssets.add(recAsset.id);
             }
             Set<id>  parentAssetIds = new set<id>();
             parentAssetIds.addAll(assetHierarchy.values());
             for(id root_AssetId : RootAssets){
             if(!parentAssetIds.contains(root_AssetId))
                childAssets.add(root_AssetId);
             }                

Because I'm not familiar with your naming conventions, you'll need to fix the logic in the if statement below yourself and should be able to do that with the changes that I've made above.

I'd only expect the 'else' to apply in the if-else that's marked below as well. If so, you'll want to remove the first condition and just make it an if statement.

             for(Asset a : assetsList)
             {

                 /*@@ you'll need to fix this if statement because of the naming @@*/
                 if(childAssets.contains(a.id)){
                 if(!(opp.name.startsWith('SP Renewal')) && !(opp.name.startsWith('OEM Renewal')) && a.GlobalCare_Level__c != 'Solution Provider' && a.GlobalCare_Level__c != 'OEM' && a.type__c!= 'SP' && a.type__c!='OEM')
                 {

                        Opportunity_Asset__c  oppAsset = new Opportunity_Asset__c();
                        oppAsset.Opportunity__c = opp.Id;
                        oppAsset.Asset__c = a.Id;
                        gcExpectedAmount = gcExpectedAmount + a.Asset_Value__c;
                        oppAssetList.add(oppAsset);


                 }                 
                }
             }

             /*@@ I'd expect only the 'else' statement to apply @@*/
             if(opp.name.startsWith('SP Renewal') || opp.name.startsWith('OEM Renewal')){
                 opp.Expected_Revenue__c = spOEMExpectedAmount;   
             }else{
                 opp.Expected_Revenue__c = gcExpectedAmount;
             }
             //Changes for US-01505 Complete

             toBeUpdatedList.add(opp);
             accList.clear();
         }
         update toBeUpdatedList;
         insert oppAssetList;
        // insert subAssetList; <-- previously commented

    }

I'll leave it to you to finish this and debug it yourself. You'll also need to update your test class for this too. See the trailhead modules on asynchronous batch if you don't know how.

EDIT:

Your 120 days is currently controlled by what appears to be a custom setting in the Database.QueryLocator start method using the Custom_Values__c list object. There's a 'DaysBeforeExpiry' value that can be set which should hold the value you're looking for. That value is converted to an integer. If you want to hard code it to 120 days, remove the 1st two lines in the method below and replace =NEXT_N_DAYS:days in the query with =NEXT_N_DAYS:120.

Note also that I've changed the original query from '=' to '<=' since I don't know how frequently you'll run this batch class and I wouldn't want to rely on equality existing. The Processed_for_Renewal__c = false condition should prevent you from returning records that have already been processed.

global Database.QueryLocator start(Database.BatchableContext BC)
{

    cvObj = Custom_Values__c.getValues('DaysBeforeExpiry');
    days = integer.valueOf(cvObj.Value__c); // set to 120
    //Query for all the service contracts that expire in the next 120 days and which are not already processed.
    query = 'Select id,name,AccountId,EndDate,Processed_for_Renewal__c,Type__c FROM ServiceContract WHERE Processed_for_Renewal__c = false AND (Type__c = \'Software GlobalCare Contract\' OR Type__c = \'Solution Provider Contract\' OR Type__c = \'OEM Contract\') AND EndDate <= NEXT_N_DAYS:days';
    return Database.getQueryLocator(query);
 }
2
  • @crmprogdev..Thank you so much your help..Can you please help me to Soql For this.As i mentioned above we need renew the opportunity after one year.How can i use specific logic in the Soql. Thanks Jan 31, 2017 at 6:27
  • 1
    See my edited answer. Thanks for posting on SFSE. Community etiquette is to upvote well thought out questions and answers if you have sufficient reputation plus help the rest of the community by marking posts as solving your question if they helped you resolve your issue.
    – crmprogdev
    Jan 31, 2017 at 16:31

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .