The trigger code is having lot of redundancy, So I modified it.
before posting the modified code few lines of explanation what I changed.
you collected account Ids twice and did the condition check twice where once is more enough. I am also made changes to the test class, where Task data is provided which is not required because the trigger has to create it once the necessary conditions are met.
I am expecting this trigger should work for you, if there is any errors feel free to post as a comment.
trigger OpportunityAfter on Opportunity (after update){
Set AccountIDs = new Set();
for(Opportunity o : trigger.new){
if(opp.StageName == 'Closed - Lost' && opp.Business_Line__c == 'Workforce' &&
opp.Survey_Type__c != 'APulse' && trigger.oldMap.get(opp.Id).StageName != opp.StageName){
AccountIDs.add(o.AccountId);
}
}
Task[] tasksToInsert = new Task[]{};
for (Account acc : [Select Id, Name, OwnerId from Account where Id IN :AccountIds]){
for (Opportunity opp : trigger.new){
if(opp.AccountId == acc.Id){
Task tas = new Task();
tas.WhatId=acc.Id;
tas.OwnerId=opp.OwnerId;
tas.Subject='Follow-up: ' + opp.name + ' - ' + acc.name;
tas.ActivityDate=(opp.CloseDate + 180);
tas.Description='Follow up to see how engagement initiatives are going and if Avatar can help. Refer to lost opportunity: ' + opp.name;
tasksToInsert.add(tas);
}
}
}
if(tasksToInsert != null && !tasksToInsert.isEmpty())
Database.insert(tasksToInsert);
}
@isTest
public class TestClass {
static testMethod void myUnitTest() {
//Set up user
User u1 = [SELECT Id FROM User WHERE Email='abc@sampleemail.com'];
test.startTest();
//Run As U1
System.RunAs(u1){
System.debug('Testing trigger... (single record validation)');
//NEW Account record
Account acc = new account();
acc.Name = 'Test Account';
acc.Industry = 'Healthcare';
insert acc;
//NEW Opportunity record
Opportunity opp = new Opportunity();
opp.Name = 'Test Opportunity';
opp.CloseDate = System.today();
opp.StageName = 'S1 - Investigative';
opp.Type = 'Repeat Business - Wrkforce HC';
opp.AccountId = acc.Id;
opp.Survey_Type__c = 'Product Name';
insert opp;
opp.StageName = 'Closed - Lost';
opp.Reason_Won_Lost__c = 'Relationship';
opp.Business_Line__c = 'Workforce' //added this line to meet the if condition while collecting the set of id in the trigger.
update opp;
test.stopTest();
// Here I am expecting the task has been inserted and doing a query for it and check it in the assert statement.
Task testTask=[Select Id, WhatId from Task where WhatId=:acc.Id];
//Validate single insert
System.assertEquals(acc.Id, testTask.WhatId);
}
}
}
Welcome to SF.SE Marting. A map contains a key and a value. What I'm gathering from your example is that you want to update values containing ProductMap__c.Former__c
to ProductMap__c.Actual__c
. I'm also gathering that ProductMap__c.Former__c
values will be unique, but ProductMap__c.Actual__c
values will not. In a map, the keys need to be unique while the values do not. That means your map needs to be something like
map<string,string>ProdMapNewOld = new map<string, string>();
You'd use ProductMap__c.Former__c
as the key and ProductMap__c.Actual__c
as the value for each key when assembling the map. The pattern is to use mapname.put(key,value).
When updating your Opportunities, I'd recommend your do your query as a Map. That way it looks like:
map<Id,Opportunity> oppsMap = new map<id,Opportunity([select id, name, Product__c from Opportunity where Product__c != '']);
You can then do
For(Id oid:oppsMap.keyset()){
If(ProdMapNewOld.containsKey(oppsMap.get(oid).Product__c)){
string tempstr = ProdMapNewOld.get(oppsMap.get(oid).Product__c;
oppsMap.get(oid).Product__c = tempstr;
}
}
You can also add them to a new list or whatever you wish in order to update the Opps. This is just to give you the concept. In the above, you need to get the key before you can reassign the value.
Edit
//I tried to fill in the map with select, but it doesn't work, is that possible?
//Map pm = new Map([SELECT Former__c, Actual__c FROM ProductMap__c]);
First, you need to declare the map properly as
map<string,string> pm
. But in this case, you're querying an object, so you'd need to get the list and then create the map. I'm guessing your map probably would be created by doing something like this:
map<string,string>pm = new map<string,string>();
map<Id,ProductMap__c>pmc = new map<Id,ProductMap__c>([select Id, Former__c, Actual__c FROM ProductMap__c]);
//This query returns all of your old and new values in a map by record Id
//Now, you can iterate on the map's Ids to "put" the pairs into the map you want to use
for(Id i:pmc.keyset()){
pm.put(pmc.get(i).Former__c,pmc.get(i).Actual__c);
}
And yes, adding your values to a new list is a perfectly acceptable way to do what you wanted to do, however, using your code, you'd have wanted to do it as follows:
//List of opportunities to update
List ou = new List();
//Compare products with map of products and store new value
For(Id oid : om.keyset()){
If(pm.containsKey(om.get(oid).Product__c)){
Opportunity o = new Opportunity(Id = oid);
o.ProdPick__c = pm.get(om.get(oid).Product__c);
//store it in the new picklist
//you could also have simply done this to pm and did the update to pm
ou.add(o);
}
}
update ou;
Best Answer
Maps work as a key-value type thing. They take a given key, and associate it to a single value (the "value" part of the map can itself be a collection, but let's keep it simple here).
What you have here is a
Map<Id, Opportunity>
. The key is the Opportunity Id, and the value is the entire Opportunity record.In this case, the thing you're getting back from the map is an SObject (an
Opportunity
, to be more specific), and you access the individual fields using dot-notation like you would with any other object.The other part of this is that you need to get an Id from your
Task
(that hopefully points to anOpportunity
, and there are several methods to check that which are beyond the scope of your question). Unless you're using a custom field, that's probably theWhatId
field on the Task.You could do all of this on a single line, but to illustrate my points, I'll keep it on multiple lines. The gist of it is that you want to do something like
Again, you use the map key to get the corresponding value, and since your value is an SObject, you access fields like you would with any other SObject variable (using dot notation).