1

I have a query for Opportunity records which are retrieved based on Account Id. I am not sure how I can remove them out of the for loop

I had to use the Opportunity for each account in the list, because for each Account I need to make the dml operation for Opportunity for each Opportunity record.

I am storing the Account Ids based on the other operation in AcList.

if(AcLst.size()>0){
    for(Id a: AcLst){
        List<Opportunity> ol = [SELECT Id, StageName, AccountId, (SELECT Id,Product2.Name FROM OpportunityLineItems WHERE Product2.Name like :prodname) FROM Opportunity WHERE AccountId IN :AcLst];
        for (Opportunity o : ol) {
            oppList = mapAccountIdOpportunityList.get(o.AccountId);        

            if (oppList == null) { 
                for(String a: AcCon.keyset()){
                    for(Id c : AcCon.values()){ 
                        Opportunity opp = new Opportunity();
                        opp.Name = 'Test One';
                        opp.AccountId = a;
                        opp.Contact__c = c;
                        opp.CloseDate= Date.newInstance(2022, 03, 01);
                        opp.StageName='Proposal';
                        mapAccountIdOpportunityList.put(opp.AccountId, oppList);
                        oppList.add(opp);
                    }
                }
            }
        }
    }
}

// this list says null even after adding opp

If I create a new list and then try adding opp, it adds the opp twice

2
  • for(String a: AcCon.keyset()){ then for(Id c : AcCon.values()){ is surely wrong. Once iterating over the keySet, you should use such key to retrieve the related element from the map. You shouldn't do an inner loop iterating over the values, otherwise you're getting the same contactId multiple times, even for the wrong accounts.
    – RubenDG
    Commented Jan 5, 2020 at 11:01
  • Yep I figured it out. Thanks again
    – user64977
    Commented Jan 5, 2020 at 17:58

1 Answer 1

7

Since you already have a list of Ids (AcLst) you could query every Opportunity related to those Accounts, then populate a Map using the accountId as key and the list of opportunity as value.

Map<Id, List<Opportunity>> mapAccountIdOpportunityList = new Map<Id, List<Opportunity>>();
for (Opportunity o : [SELECT Id, StageName, AccountId, (SELECT Id,Product2.Name FROM OpportunityLineItems WHERE Product2.Name like :prodname) FROM Opportunity WHERE AccountId IN :AcLst]) {
    List<Opportunity> oppList = mapAccountIdOpportunityList.get(o.AccountId);
    // If it's the first opportunity retrieved for the related account
    if (oppList == null) { 
        oppList = new List<Opportunity>();
        mapAccountIdOpportunityList.put(o.AccountId, oppList);
    }
    oppList.add(o);
}

for (Id accId : AcLst) {
    List<Opportunity> oppList = mapAccountIdOpportunityList.get(accId);
    if (oppList != null) {
        // the desired behavior
    }
}

Example: you have three accounts: A1, A2 and A3.
A1 has two opportunities: O10 and O11.
A2 has no opportunity at all.
A3 has only one opportunity: O30.

After the for-loop the map will contain two entries:

  1. key: A1 - value: [O10, O11]
  2. key: A3 - value: [O30]

If you're going to loop on AcLst to retrieve the opportunities, you'll have to check whether there is such key into the map.
To do so, you could either wrap your code within an if (map.containsKey(accountId)) or simply get the list from the map then check if it's != null.

Added my code on the top

Could you please explain in which cases you have to create a new opportunity?
From your code I understand that you have to create it for every account in AcCon map. If not, if you want to create a new opportunity only for the accounts which have no one, such code (and therefore the one I wrote below) must be changed.

Anyway here's how I would refactor it:

if(AcLst.size()>0){
    // Create a map
    Map<Id, List<Opportunity>> mapAccountIdOpportunityList = new Map<Id, List<Opportunity>>();
    // Populate the map getting every opportunity related to the account in the list.
    for (Opportunity o : [SELECT Id, StageName, AccountId, (SELECT Id,Product2.Name FROM OpportunityLineItems WHERE Product2.Name like :prodname) FROM Opportunity WHERE AccountId IN :AcLst]) {
        List<Opportunity> oppList = mapAccountIdOpportunityList.get(o.AccountId);
        // If it's the first opportunity retrieved for the related account
        if (oppList == null) {
            oppList = new List<Opportunity>();
            mapAccountIdOpportunityList.put(o.AccountId, oppList);
        }
        oppList.add(o);
    }

    // If you want to create a new opportunity for every account in AcCon
    List<Opportunity> oppToInsert = new List<Opportunity>();
    // since the date is always 2022-03-01, there is no need to create a new instance for every iteration
    Date expectedCloseDate = Date.newInstance(2022, 03, 01);
    for(Id a: AcCon.keyset()){
        Id c = AcCon.get(a);
        List<Opportunity> oppList = mapAccountIdOpportunityList.put(a, oppList);
        // If no opportunity for this account has been retrieved by the previous query
        if (oppList == null) {
            oppList = new List<Opportunity>();
            mapAccountIdOpportunityList.put(a, oppList);
        }
        Opportunity opp = new Opportunity();
        opp.Name = 'Test One';
        opp.AccountId = a;
        opp.Contact__c = c;
        opp.CloseDate = expectedCloseDate;
        opp.StageName ='Proposal';
        oppList.add(opp);
        oppToInsert.add(opp);
    }

    // If it ends here, you should uncomment the next line
    // insert oppToInsert;
}
6
  • In my scenario, I might have opportunity for Account[0] and do not have an opportunity for Account[1] in the same list. I would like to cover both the scenarios. How will I be able to do that? When I add this criteria, it works fine for the first Account in the list when the Opportunity is null, but fails when Opportunity is not nul
    – user64977
    Commented Jan 4, 2020 at 22:12
  • If you won't have an opportunity for Account[1] then no opportunity will be return for that account by the query, so that accountId will never be put in the map. This means that, when you're going to use the map, mapAccountIdOpportunityList.get(Account[1]) will retrun null. If you want to avoid the null check, you could use .containsKey() method.
    – RubenDG
    Commented Jan 4, 2020 at 22:22
  • Got it. Thank you for explaining. I tried the same logic. if (oppList == null) { oppList = new List<Opportunity>(); mapAccountIdOpportunityList.put(o.AccountId, oppList); } oppList.add(o); // this list says null even after I add an opportunity for insert When I changed the variable name from oppList, it adds same element twice. Not sure what's going wrong
    – user64977
    Commented Jan 5, 2020 at 2:48
  • Added my code on the top
    – user64977
    Commented Jan 5, 2020 at 2:51
  • @user64977 you missed oppList = new List<Opportunity>();. You have to add it as first line in the if block if (oppList == null) {. Moreover you didn't removed the query from your loop on AcLst.
    – RubenDG
    Commented Jan 5, 2020 at 10:50

You must log in to answer this question.

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