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
Roberto Gimenez 1Roberto Gimenez 1 

Subscribe to Change Events Using an Apex Trigger

Hi,

I'm trying to accomplish OpportunityChangeTrigger challenger from trailhead but I found one error and I don't know how to address it.
The error is:
The trigger doesn't create the expected task for opportunities whose stage has been updated to 'Closed Won'.

This is my code for the trigger:
 
trigger OpportunityChangeTrigger on OpportunityChangeEvent (after insert) {
    List<Opportunity> opps = new List<Opportunity>();
    List<Task> taskList = new List<Task>();
    for(OpportunityChangeEvent event : Trigger.New){
        EventBus.ChangeEventHeader header = event.ChangeEventHeader;
        if(header.changeType == 'UPDATE' && event.isWon){
			Task task = new Task();
            task.subject = 'Follow up on won opportunities: ' +header.recordIds;
            tasklist.add(task);
        }
    }
    if(taskList.size()>0){
        insert taskList;
    }

}

And this is the  test class
@isTest
public class TestOpportunityChangeTrigger {
    @isTest static void testCreateAndUpdateEmployee(){
        Test.enableChangeDataCapture();
        insert new Opportunity(Name='Sell 100 Widgets', StageName='Prospecting', CloseDate=Date.today().addMonths(3));
        Test.getEventBus().deliver();
        Opportunity[] oppList = [SELECT Id,StageName FROM Opportunity];
        for (Opportunity opp : oppList){
          opp.StageName = 'Closed Won';
          update opp;
          Test.getEventBus().deliver();
      }
        Task[] taskList = [SELECT Id FROM Task];
        System.assertEquals(1, taskList.size(),'The change event trigger did not create the expected task.');
    }
}



 
Best Answer chosen by Roberto Gimenez 1
Alain CabonAlain Cabon
@Roberto 

The robot is a bit stupid:    event.isWon == true

(... while the documentation clearly says that you must be "concise" )

All Answers

Alain CabonAlain Cabon
@Roberto Gimenez 
 
  • The new tasks should be linked to the opportunities ( field: WhatId ) and the array header.recordIds contains the ids of the opportunities.

This requirement is clearly missing.

 
Roberto Gimenez 1Roberto Gimenez 1
Thanks for your reply @Alain Cabon,

You're right. I was not linking task to the opportunity, so I added the following line to my code

task.WhatId = header.recordIds[0];

Unfourtunatly, it's still not passing the challenge... I can't figure out why.

This is how my trigger and class are now.
@isTest
public class TestOpportunityChangeTrigger {
    @isTest static void testCreateOpportunity(){
        Test.enableChangeDataCapture();
        insert new Opportunity(Name='Sell 100 Widgets', StageName='Prospecting', CloseDate=Date.today().addMonths(3));
        Test.getEventBus().deliver();
        Opportunity[] oppList = [SELECT Id,StageName FROM Opportunity];
        for (Opportunity opp : oppList){
          opp.StageName = 'Closed Won';
          update opp;
          Test.getEventBus().deliver();
      }
        Task[] taskList = [SELECT Id FROM Task];
        System.assertEquals(1, taskList.size(),'The change event trigger did not create the expected task.');
    }
}
 
trigger OpportunityChangeTrigger on OpportunityChangeEvent (after insert) {
    List<Opportunity> opps = new List<Opportunity>();
    List<Task> taskList = new List<Task>();
    for(OpportunityChangeEvent event : Trigger.New){
        EventBus.ChangeEventHeader header = event.ChangeEventHeader;
        if(header.changeType == 'UPDATE' && event.isWon){
			Task task = new Task();
            task.WhatId = header.recordIds[0];
            task.subject = 'Follow up on won opportunities: ' +header.recordIds;
            tasklist.add(task);
        }
    }
    if(taskList.size()>0){
        insert taskList;
    }

}

 
Alain CabonAlain Cabon
@Roberto Gimenez 

There is a list of opportunities potentially with one task by opportunity probably but with each time the complete list of ids in the subject moreover.

There is also a commit User who should be missing in the requirements ( new OwnerId of the task ).

public List<String> recordids {get; set;}:   
One or more record IDs for the changed records. Typically, this field contains one record ID. But if the same change occurred in multiple records of the same object type during the same transaction, Salesforce groups the change notifications and sends one change event for all affected records. In this case, the recordIds field contains an array of record IDs for all records that have the same change.

commitUser String  = The ID of the user that ran the change operation.
 
for(OpportunityChangeEvent event : Trigger.New){
      ...
      for ( String oppId : header.recordIds) {
         ...
         task.WhatId = oppId;
         task.OwnerId = header.CommitUser;
         ...
      }
      ...
}

https://developer.salesforce.com/docs/atlas.en-us.change_data_capture.meta/change_data_capture/cdc_event_fields_header.htm

 
Roberto Gimenez 1Roberto Gimenez 1
Thanks for the clarification on EventBus @Alain Cabon,

I modified my class, for adding all the new tasks for all the records in the EventBus, adding related ID to opportunity and owner.
I think that this should work, but still it's not passing the challenge... that's weird.
 
trigger OpportunityChangeTrigger on OpportunityChangeEvent (after insert) {
    List<Opportunity> opps = new List<Opportunity>();
    List<Task> taskList = new List<Task>();
    for(OpportunityChangeEvent event : Trigger.New){
        EventBus.ChangeEventHeader header = event.ChangeEventHeader;
        if(header.changeType == 'UPDATE' && event.isWon){
            for(String oppId: header.recordIds){
				Task task = new Task();
            	task.WhatId = oppId;
                task.OwnerId= header.CommitUser;
            	task.subject = 'Follow up on won opportunities: ' +header.recordIds;
            	tasklist.add(task);
            }
        }
    }
    if(taskList.size()>0){
        insert taskList;
    }

}



 
Alain CabonAlain Cabon
@Roberto 

The robot is a bit stupid:    event.isWon == true

(... while the documentation clearly says that you must be "concise" )
This was selected as the best answer
Roberto Gimenez 1Roberto Gimenez 1
@Alain Cabon. Good point! Thanks!
SAKTHIVEL MSAKTHIVEL M
Hi All, 

Like to know about Async Apex trigger as a part of updated summer 19, please view
https://medium.com/@sakthivelmadesh/asynchronous-apex-triggers-in-summer-19-release-2bae1b754879
https://techforceservices.com.au/salesforce/asynchronous-apex-triggers/

Thanks & Regards,
Sakthivel Madesh
Jeff Kaplan 16Jeff Kaplan 16

i keep getting this error when trying to comlpete badge~

Challenge not yet complete in My Trailhead Playground 1
The test method doesn’t insert an opportunity with the correct fields. Double check the Name, StageName, and CloseDate and try again.
Dmitry ZorinDmitry Zorin
Disabling other Apex triggers for Opportunity table did the trick for me.
Anand PeriAnand Peri
Make sure you have added the Opportunity to the CDC tracking in the setup. After doing that everything worked out.