0

Whenever an update the Opportunity fields create a new brand task along with Opportunity name. For that we have written a trigger working as expected.

But once create a task if we update the description field it should create a new task not overriding the existing task.

Every time Step(OpportunityField) or Description(OpportunityField) changes, a new task should be created

Example: Step = 'Legal', Description = 'test'

If I again select Step = 'Legal' and Description = 'test1234' a new task should be created. Currently its updating test to 'test1234'.

Please Find my below trigger and let us know where we made the mistakes.

Trigger Task on Opportunity(after insert,after update){
    Set<Id> opIds = new Set<Id>();
    List<Task> taskList = new List<Task>();
    List<Opportunity> Opps = Trigger.new;
    List<Opportunity> taskOps = [Select Id,Steps__c,Description__c from Opportunity where Id in :opIds];

    if(Trigger.isInsert){
        for (Opportunity Opp: Opps){
            Task t = new Task();
            t.WhatId = opp.Id;
            t.Subject = 'Help' + ': ' +opp.Steps__c + ': '  + opp.Name; 
            t.Description = opp.Description__c;  
            t.ActivityDate = System.Today()+7;     
            taskList.add(t);
        }
    }

    //Update 
    if(Trigger.isUpdate){
        Map<Id,Opportunity> oldOppMap = Trigger.oldmap;
        for (Opportunity Opp: Opps){
        // description got changed
            if(Opp.Steps__c != oldOppMap.get(opp.Id).Steps__c ){
                Task t = new Task();
                t.WhatId = opp.Id;       
                t.Subject = 'Help' + ': ' +opp.Steps__c + ': '  + opp.Name; 
                t.Description = opp.Description__c;  
                t.ActivityDate = System.Today()+7;           
                taskList.add(t);
            }
        }
    }

    insert taskList;
}

Thanks in Advance

  • Your comment says description got changed, but you are comparing Steps__c. Is that a typo? – IllusiveBrian Aug 12 '16 at 13:01
  • @IllusiveBrian: Yes,That is one of the single picklist Lov – Devi Aug 12 '16 at 13:52
2

This situation is the perfect use case for Selector. It's also a good time to:

  • Learn how to implement a Service Layer [1]
  • Understand the Handler Pattern [2] and find an implementation that suits you.
  • Learn how to properly handle errors [3]

With Selector, the service layer is fairly simple:

public with sharing class OpportunityServices
{
    public static Select.Filter hasStepChanged()
    {
        return Select.Field.hasChanged(Opportunity.Steps__c);
    }
    public static void createTasks()
    {
        // move current implementation here
        // add try/catch
    }
}

Then your handler pattern can look something like:

public with sharing class OpportunityTriggerHandler
{
    @TestVisible static Boolean bypassTrigger = false;

    final List<Opportunity> newRecords;
    final Map<Id, Opportunity> oldMap;
    public NoteTriggerHandler(List<Opportunity> newRecords, Map<Id, Opportunity> oldMap)
    {
        this.newRecords = newRecords;
        this.oldMap = oldMap;
    }

    public void afterInsert()
    {
        if (bypassTrigger) return;

        OpportunityServices.createTasks(newRecords);
    }

    public void afterUpdate()
    {
        if (bypassTrigger) return;

        OpportunityServices.createTasks(
            OpportunityServices.hasStepChanged().filter(newRecords, oldMap)
        );
    }
}

Then your trigger simply needs to implement the handler:

trigger Opportunity on Opportunity (after insert, after update)
{
    OpportunityTriggerHandler handle = new OpportunityTriggerHandler(trigger.new, trigger.oldMap);
    if (trigger.isAfter)
    {
        if (trigger.isInsert) handle.afterInsert();
        if (trigger.isUpdate) handle.afterUpdate();
    }
}

[1] Apex Enterprise Patterns - Service Layer
[2] Trigger Frameworks and Apex Trigger Best Practices
[3] An Introduction to Exception Handling

  • have you ever had discussions with Andy Fawcett about adding this to the Force.com Enterprise Architecture (fflib) GitHub. As I understand it, Select is primarily to filter sobjects already in memory, not to do SOQL. And there's nothing like this in fflib - the fflib_SObjectSelector and associated fflib_QueryFactory class are for SOQL reuse – cropredy Aug 12 '16 at 20:06
  • @crop No I've never talked to him at all, actually. I'd love to, but he's a bit of a big fish for me! Regardless, I had no role in the creation of this package, I just love it and use it religiously. It's king of SOC. – Adrian Larson Aug 16 '16 at 2:45

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for?Browse other questions tagged or ask your own question.