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
Dave BerenatoDave Berenato 

Save Button is updating Multiple Records in Apex Table

I have an Apex Class that generates a List of Events:
 
Public class AgentAppointmentsPast30 {
    
	String sortOrder = ' Who.Name ';
    String ascendingOrDescending = ' ASC ';
    String UserID = UserInfo.getUserId();
    String dateTimeFormat = DateTime.now().format('yyyy-MM-dd\'T\'hh:mm:ss\'Z\'');
    
    String myRequest = 'SELECT id, Owner.Id, type, subject, Decision_Made_by_Client__c, Appointment_Result__c, Reason_for_Cancellation_No_Show__c,'+
                ' WhoId, Who.Name, Appoint_Set_By__c, ActivityDateTime, No_Show_Cancelled_Disposition__c,Request_to_Shuffle__c,Contact_Name__r.Actual_Sale_Date__c, Contact_Name__r.Foreclosure_Status__c, Contact_Name__r.FAIR_Packet__c,'+ 
        	    ' Contact_Name__c, Contact_Name__r.Name, Contact_Name__r.Id, Contact_Name__r.Account.Property_Address__c,Contact_Name__r.Account.Name, Contact_Name__r.Account.Id'+
                ' FROM Event ' + 
                ' WHERE Owner.Id = \''+UserId+'\' And RecordTypeId = \'0126A0000004Qle\' AND ( ActivityDateTime = LAST_N_DAYS:30 OR (ActivityDateTime = TODAY And Type != \'Appointment - Booked\' And Type != \'Appointment - Confirmed\' ))'+
				' ORDER BY ';

    
    	public void sortByContact() 	{this.sortOrder = ' Who.Name ';
                                   if(ascendingOrDescending ==' ASC ') 
                                   {ascendingorDescending = ' DESC ';}
                                   else{ascendingorDescending = ' ASC ';}}
    
   		public void sortByActivityDateTime() 	{this.sortOrder = ' ActivityDateTime ';
                                   if(ascendingOrDescending ==' ASC ') 
                                   {ascendingorDescending = ' DESC ';}
                                   else{ascendingorDescending = ' ASC ';}}
    
    	public void sortByType() 	{this.sortOrder = ' Type ';
                                   if(ascendingOrDescending ==' ASC ') 
                                   {ascendingorDescending = ' DESC ';}
                                   else{ascendingorDescending = ' ASC ';}}
    
    	public void sortBySubject() 	{this.sortOrder = ' Subject ';
                                   if(ascendingOrDescending ==' ASC ') 
                                   {ascendingorDescending = ' DESC ';}
                                   else{ascendingorDescending = ' ASC ';}}
    
    	public void sortByOutcome() 	{this.sortOrder = ' Decision_Made_by_Client__c ';
                                   if(ascendingOrDescending ==' ASC ') 
                                   {ascendingorDescending = ' DESC ';}
                                   else{ascendingorDescending = ' ASC ';}}
    
    	public void sortByOutcomeNotes() 	{this.sortOrder = ' Appointment_Result__c ';
                                   if(ascendingOrDescending ==' ASC ') 
                                   {ascendingorDescending = ' DESC ';}
                                   else{ascendingorDescending = ' ASC ';}}
    
    	public void sortByNoShowDisp() 	{this.sortOrder = ' No_Show_Cancelled_Disposition__c ';
                                   if(ascendingOrDescending ==' ASC ') 
                                   {ascendingorDescending = ' DESC ';}
                                   else{ascendingorDescending = ' ASC ';}}
    
    	public void sortByNoShowReason() 	{this.sortOrder = ' Reason_for_Cancellation_No_Show__c ';
                                   if(ascendingOrDescending ==' ASC ') 
                                   {ascendingorDescending = ' DESC ';}
                                   else{ascendingorDescending = ' ASC ';}}
    
    	public void sortByAddress() 	{this.sortOrder = ' Contact_Name__r.Account.Name ';
                                   if(ascendingOrDescending ==' ASC ') 
                                   {ascendingorDescending = ' DESC ';}
                                   else{ascendingorDescending = ' ASC ';}}
    
    	public void sortByPacket() 	{this.sortOrder = ' Contact_Name__r.FAIR_Packet__c ';
                                   if(ascendingOrDescending ==' ASC ') 
                                   {ascendingorDescending = ' DESC ';}
                                   else{ascendingorDescending = ' ASC ';}}



        List<Event> events;
     	public List<Event> getEvents() {
		events = new List<Event>();
        events = Database.query(myRequest + sortOrder + ascendingOrDescending);
        system.debug(events);
        return events;}

  		public void saveAndReturn() {
        update events;        
        }
}
I use the following test class:
 
@isTest
public class AgentAppointmentsPast30Test {
    
    static testMethod void TestApptPast30(){
        
        Account Testaccount = new account();
       	Testaccount.name = '123 Test Drive';
        Testaccount.RecordTypeId = '0126A0000004Qo4';
        Testaccount.APN__c = '123';
        Testaccount.OwnerId = '0056A000000HeMZ';
        insert Testaccount;
        
        Contact Testcontact = new contact();
        Testcontact.Lastname = 'Berenato';
        Testcontact.AccountId = Testaccount.Id;
        Testcontact.RecordTypeId = '0126A0000004QlU';
        Testcontact.OwnerId = '0056A000000HeMZ';
        insert Testcontact;
        
        Event Testevent = new event();
		Testevent.WhoId = Testcontact.id;        
        Testevent.Subject='Test';
        Testevent.type='Appointment - Booked';
        Testevent.RecordTypeId = '0126A0000004Qle';
        Testevent.ActivityDateTime=date.valueOf('2017-10-17 11:00:00z');
        Testevent.DurationInMinutes=120;
        insert Testevent;
        
        AgentAppointmentsPast30 at = new AgentAppointmentsPast30();
      	List<Event> list1 = at.getEvents();
        at.saveAndReturn();
        at.sortByActivityDateTime();
        at.sortByAddress();
        at.sortByContact();
        at.sortByNoShowDisp();
        at.sortByNoShowReason();
        at.sortByOutcome();
        at.sortByOutcomeNotes();
        at.sortByPacket();
        at.sortBySubject();
        at.sortByType();
        system.assertEquals(1, list1.size());
    }
}

I've never been able to have the event from the Test Class meet the criteria from the Apex Class, I always get the error:

System.AssertException: Assertion Failed: Expected: 1, Actual: 0

Even if I remove everything from the WHERE clause except the getUserId.

However, my Visualforce page in the Sandbox seems to be working fine, minus one major error.

If I assign two events to myself, I see them both in the Apex PageBlockTable.

User-added image

If I edit the event that is farther in the future, I click the Save Button I added to the table.

User-added image

The page reloads:

User-added image

The records are sorted by ActivityDateTime.

And then I manually refresh the page.

User-added image

And the event that was originally farther in the past gets automatically updated to match the new events edit.

As if the Save button is saving the new value to all of the records.

I'm totally stumped on how this is happening. I can't seem to find any workflow rule that would suggest this, and I've tested it several times outside of the PageBlockTable (in the Event's Detail page) and can't find proof that it updates both records independently.

Does anyone know what might be causing this, or why my Apex Class can't pass its test?



 
Alain CabonAlain Cabon
Hi Dave,

You need to force the use of a precise user for your test using runAs. That is the missing information here.

Using the runAs Method: Generally, all Apex code runs in system mode, where the permissions and record sharing of the current user are not taken into account. The system method runAs enables you to write test methods that change the user context to an existing user or a new user so that the user’s record sharing is enforced. The runAs method doesn’t enforce user permissions or field-level permissions, only record sharing.

https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex_testing_tools_runas.htm

 FROM Event ' +
       ' WHERE Owner.Id = \''+UserId+'\' And RecordTypeId = \'0126A0000004Qle\' AND ( ActivityDateTime = LAST_N_DAYS:30 OR (ActivityDateTime = TODAY And Type != \'Appointment - Booked\' And Type != \'Appointment - Confirmed\' ))'+
       ' ORDER BY ';


You can create the profile and the user from scratch as here (best method): 
http://https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex_testing_tools_runas.htm

... or you can get an existing user directly if you are sure that this user exists for each org ( sandbox and production ) like below:

 User myuser = [select id,name from user where name = 'Alain Cabon'];
 system.debug('user:' + myuser.id);

System.runAs(myuser) {
// put all your test code here : 
The following code runs as user 'u'
...
        System.debug('Current User: ' + UserInfo.getUserName());
        System.debug('Current Profile: ' + UserInfo.getProfileId());
        System.debug('Current User Id ' + UserInfo.getUserId());

        Contact Testcontact = new contact();
        Testcontact.Lastname = 'Berenato';
        Testcontact.AccountId = Testaccount.Id;
        Testcontact.RecordTypeId = '0126A0000004QlU';
        Testcontact.OwnerId UserInfo.getUserId();
        insert Testcontact;

        Event Testevent = new event();
        Testevent.OwnerId =  UserInfo.getUserId();
        Testevent.WhoId = Testcontact.id;       
        Testevent.Subject='Test';
        Testevent.type='Appointment - Booked';
        Testevent.RecordTypeId = '0126A0000004Qle';   // the recordtype is also an exception for the use of ID directly
        Testevent.ActivityDateTime=date.valueOf('2017-10-17 11:00:00z');
        Testevent.DurationInMinutes=120;
        insert Testevent;
...
 }

That should work now.
 
User myuser = [select id,name from user where name = 'Alain Cabon'];
 system.debug('user:' + myuser.id);

System.runAs(myuser) {
// put all your test code here : The following code runs as user 'myuser'
...
        System.debug('Current User: ' + UserInfo.getUserName());
        System.debug('Current Profile: ' + UserInfo.getProfileId());
        System.debug('Current User Id ' + UserInfo.getUserId());

        Contact Testcontact = new contact();
        Testcontact.Lastname = 'Berenato';
        Testcontact.AccountId = Testaccount.Id;
        Testcontact.RecordTypeId = '0126A0000004QlU';
        Testcontact.OwnerId = UserInfo.getUserId();
        insert Testcontact;

        Event Testevent = new event();
        Testevent.OwnerId =  UserInfo.getUserId();
        Testevent.WhoId = Testcontact.id;       
        Testevent.Subject='Test';
        Testevent.type='Appointment - Booked';
        Testevent.RecordTypeId = '0126A0000004Qle';   // the recordtype is also an exception for the use of ID directly
        Testevent.ActivityDateTime=date.valueOf('2017-10-17 11:00:00z');
        Testevent.DurationInMinutes=120;
        insert Testevent;
...
 }

Regards
Alain
Dave BerenatoDave Berenato
Hi Alain,

As always, thank you for your diligent response. I appreciate you coming to my rescue on these matters.

I tried both the New User and the grab and existing user method, and both could not pass the test. I'm worried something deeper is going on with the Apex Class, as related to the editing of multiple appointments.
 
@isTest
public class AgentAppointmentsPast30Test {
    
		static testMethod void TestApptPast30(){
            
        User myuser = [select id,name from user where name = 'Dave Berenato'];
 		system.debug('user:' + myuser.id);

		System.runAs(myuser) {

        System.debug('Current User: ' + UserInfo.getUserName());
        System.debug('Current Profile: ' + UserInfo.getProfileId());
        System.debug('Current User Id ' + UserInfo.getUserId());
        
        Contact Testcontact = new contact();
        Testcontact.Lastname = 'Berenato';
        Testcontact.RecordTypeId = '0126A0000004QlU';
        Testcontact.OwnerId = UserInfo.getUserId();
        insert Testcontact;
        
        Event Testevent = new event();
        Testevent.OwnerId =  UserInfo.getUserId();
		Testevent.WhoId = Testcontact.id;        
        Testevent.Subject='Test';
        Testevent.type='Appointment - Booked';
        Testevent.RecordTypeId = '0126A0000004Qle';
        Testevent.ActivityDateTime=date.valueOf('2017-10-17 11:00:00z');
        Testevent.DurationInMinutes=120;
        insert Testevent;
        
        AgentAppointmentsPast30 at = new AgentAppointmentsPast30();
      	List<Event> list1 = at.getEvents();
        at.saveAndReturn();
        at.sortByActivityDateTime();
        at.sortByAddress();
        at.sortByContact();
        at.sortByNoShowDisp();
        at.sortByNoShowReason();
        at.sortByOutcome();
        at.sortByOutcomeNotes();
        at.sortByPacket();
        at.sortBySubject();
        at.sortByType();
        system.assertEquals(1, list1.size());
    }
}
}

This did not pass the test. I haven't made any changes to the Apex Class from above.
Alain CabonAlain Cabon
Hi Dave,

I have used "..." because I simplified but you need an account for the contact as you created first.

sortByAddress(): can failed without Account : Contact_Name__r.Account.Name ';

The filter for the event should be resolved now:

  Testevent.OwnerId =  UserInfo.getUserId();
  Testevent.RecordTypeId = '0126A0000004Qle';
  Testevent.ActivityDateTime=date.valueOf('2017-10-17 11:00:00');  // you don't need "z" even that seems to pass the compilation.

 FROM Event ' +   ' WHERE Owner.Id = \''+UserId+'\' And RecordTypeId = \'0126A0000004Qle\' AND ( ActivityDateTime =LAST_N_DAYS:30 OR (ActivityDateTime = TODAY And Type != \'Appointment - Booked\' And Type != \'Appointment - Confirmed\' ))'+
       ' ORDER BY ';

Do you see clearly the log of your test when the test failed (double clicks in the developer console)?

valueOf(stringDate): The specified string should use the standard date format “yyyy-MM-dd HH:mm:ss” in the local time zone.
https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex_methods_system_date.htm
 
Salesforce DeveloperSalesforce Developer
Hi Dave, 

For the assertion failure, please update your query to use OwnerId instead of Owner.id in the Where condition. On line 8: 
 
String myRequest = 'SELECT id, Owner.Id, type, subject, Decision_Made_by_Client__c, Appointment_Result__c, Reason_for_Cancellation_No_Show__c,'+
                ' WhoId, Who.Name, Appoint_Set_By__c, ActivityDateTime, No_Show_Cancelled_Disposition__c,Request_to_Shuffle__c,Contact_Name__r.Actual_Sale_Date__c, Contact_Name__r.Foreclosure_Status__c, Contact_Name__r.FAIR_Packet__c,'+ 
        	    ' Contact_Name__c, Contact_Name__r.Name, Contact_Name__r.Id, Contact_Name__r.Account.Property_Address__c,Contact_Name__r.Account.Name, Contact_Name__r.Account.Id'+
                ' FROM Event ' + 
                ' WHERE OwnerId = \''+UserId+'\' And RecordTypeId = \'0126A0000004Qle\' AND ( ActivityDateTime = LAST_N_DAYS:30 OR (ActivityDateTime = TODAY And Type != \'Appointment - Booked\' And Type != \'Appointment - Confirmed\' ))'+
				' ORDER BY ';

I don't have concreate answer on why it is causing issue may be in test class when you refer Owner.Id you are using a reference to another Object then accessing its Id field, whereas we have local field OwnerId on the Event object. 
Salesforce DeveloperSalesforce Developer
Also, for avoiding update on multiple records, please use wrapper class. Through wrapper class you would be able to perform operation only on the selected records not on all the records. You can follow below link for more information on wrapper class:
https://developer.salesforce.com/page/Wrapper_Class