• brianspatso
  • NEWBIE
  • 25 Points
  • Member since 2011

  • Chatter
    Feed
  • 1
    Best Answers
  • 0
    Likes Received
  • 0
    Likes Given
  • 4
    Questions
  • 9
    Replies

And I though test classes were easy....

 

I am writing a Test Class for a Trigger which updates the count of a child object in the parent object. Except the Test Class does not seem to pass.

 

The parent and child are both Accounts, where the child record is identified by a lookup field to the parent record. When I change an Account to be a child of another Account, the trigger fires an update of the count in the parent record.

 

The test class runs a very simple test, which creates two Accounts (A and B), then updates the lookup in B to point to A. This should cause the Trigger to run, which would update the count on the parent Account, A.

 

However, the test class fails, as the count on the parent is still NULL.

 

I have performed the same test manually and it works - the count on the parent Account is definitely increased.

 

So why does the Test Class fail??

 

Here is the class:

@isTest
private class TestDepartmentCountChange {

static testMethod void testCountChange() {

Account companya = new Account(Name = 'Company A', RecordTypeId = '01220000000Dqq7');
insert companya;

Account companyb = new Account(Name = 'Company B', RecordTypeId = '01220000000Dqq7', Department__c = TRUE, Parent_Company__c = companya.id);
insert companyb;

//system.assert(companya.No_of_Departments__c == NULL);

//companyb.Department__c = TRUE;
//companyb.Parent_Company__c = companya.id;
//update companyb;

system.assert(companya.No_of_Departments__c == 1);

companyb.Department__c = FALSE;
companyb.Parent_Company__c = NULL;
update companyb;

system.assert(companya.No_of_Departments__c == NULL);

}
}

 And here is the Trigger:

trigger DepartmentSumTrigger2 on Account (after delete, after insert, after undelete,
after update) {

    // When deleting a Department use the previous values
    Account[] dept;
    if (Trigger.isDelete){
        dept = Trigger.old;
    }
  
    else
        dept = Trigger.new;
    

   
    // Get a list of all Accounts that are Parent Companies
    Set<ID> acctIds = new Set<ID>();
    for (Account dep :dept) {
        acctIds.add(dep.Parent_Company__c);
    }
    
    // Fill a Map with a list of all Departments
    Map<ID, Account> departmentsForAccounts = 
    new Map<ID, Account>([SELECT Id, Parent_Company__c FROM Account WHERE Department__c = TRUE AND Parent_Company__c IN :acctIds]);
    
    // Fill a map with a list of all Parent Companies
    Map<ID, Account> acctsToUpdate = 
    new Map<ID, Account>([SELECT Id, No_of_Departments__c FROM Account WHERE Id IN :acctIds]);
    
    // For all Parent Companies            
    for (Account acct : acctsToUpdate.values()) {
        Set<ID> depIds = new Set<ID>();
        for (Account dep : departmentsForAccounts.values()) {
            if (dep.Parent_Company__c == acct.Id)
                depIds.add(dep.Id);
        }
        if (acct.No_of_Departments__c != depIds.size())
            acct.No_of_Departments__c = depIds.size();
    }

    update acctsToUpdate.values();

}



I am fairly new to Apex, and I'm trying to update a list of Contacts whenever something is changed in the parent Account. The Trigger I have written works, apart from where the Contact Owner is Inactive.

 

I could transfer the Contacts to another, active Owner and update them. But for Opportunities, I need to put the old Owner back. So my idea is to activate the Owner, update the Contact, then deactivate the Owner.

 

However, everything I try generates the Mixed DML Operation error, because I am trying to update both the Contact and User objects. I have researched this problem and found the following. It mentions that I can use the @future method inside a class, then call the class from the trigger.

 

However, I don't know where to start with this. My experience with classes is around test classes - so I'm not sure how to use a class to actually update a record permanently.

 

This is the code I have to update the Contacts:

trigger UpdateChildObjectsToDepartment on Account (after update) {

  Map<Id, Account> accountMap = new Map<Id,Account>();
  Account parent;

//For changing company TO Deparment
  for (Integer i = 0; i < Trigger.new.size(); i++){
    if (Trigger.new[i].Department__c == true && Trigger.old[i].Department__c == false) {
      accountMap.put( Trigger.new[i].Id, Trigger.new[i]);
    }
  }
  
 
  if( accountMap.size() > 0 ) {
  Set<Id> accountIds = accountMap.keySet();
  
  List<Contact> relatedContacts =
      [SELECT f.id, f.Accountid,  f.Owner.Name, f.Department__r.id, f.Ownerid, f.Owner.IsActive
      FROM Contact f
      WHERE f.Accountid IN :accountIds
      FOR UPDATE];
      
  for ( Contact child : relatedContacts ) {
        if ( accountMap.containsKey( child.Accountid ) ) {
        parent = accountMap.get( child.Accountid );
        child.Department__c = child.Accountid;
        child.Accountid = parent.Parent_Company__c;
        }
      }
      
      
update relatedContacts;
   
}}

 

The code I have written enables us to seperate Parent Companies from Departments, all within the Account object.

 

This particular code is intended to update all related records (Contacts, Opportunities and Sales Invoices) with the new Parent and Deparment details, when an Account is changed from Parent to Department.

 

The code so far is as follows:

trigger UpdateChildObjects on Account (after update) {

  Map<Id, Account> accountMap = new Map<Id,Account>();
  Account parent;
 
  for (Integer i = 0; i < Trigger.new.size(); i++){
    if (Trigger.new[i].Department__c == true && Trigger.old[i].Department__c == false) {
      accountMap.put( Trigger.new[i].Id, Trigger.new[i]);
    }
  }
  
  if( accountMap.size() > 0 ) {
  Set<Id> accountIds = accountMap.keySet();
  
  List<Contact> relatedContacts =
      [SELECT f.id, f.Accountid,  f.Owner.Name, f.Department__r.id, f.Ownerid
      FROM Contact f
      WHERE f.Accountid IN :accountIds
      FOR UPDATE];
      
  for ( Contact child : relatedContacts ) {
        if ( accountMap.containsKey( child.Accountid ) ) {
        parent = accountMap.get( child.Accountid );
        child.Department__c = child.Accountid;
        child.Accountid = parent.Parent_Company__c;
        }
      }
      

update relatedContacts;
   
}}

 Now so far, this code works fine. However, on child records where the owner is inactive it does not, obviously, work.

 

While this error is to be expected, I haven't managed to find a way to temporarily activate the owner so that I can update it.

 

Unfortunately, I cannot change the owner, as Deals need to stay with the same user.

 

I have tried something along the lines of:

  User getOwner = [SELECT Id, IsActive FROM User WHERE Id = :child.ID];

  getOwner.IsActive = True;

  update getOwner; 

 But it throws back a huge error, which I cannot decipher.

 

Funnily enough, if I swap:

  getOwner.IsActive = True;

 for:

  getOwner.Title = 'Test';

 it works fine!

 

Any advice?

 

****New Info****

 

This seems to be something to do with updating both the Contact object AND the User object - a Mixed DML Operation Error. I have done some research but the only solutions I can find are for test classes, not triggers. I think that's because it is not common to update a user from a trigger, but I cant be sure.

 

Here is an example of the problems other people have had - I just can't work out how to adapt it to my own code.

 

I am fairly new to Apex classes and triggers, but I am finding my feet with the code I have recently written.

 

Having written and tested the code I am now trying to deploy it in the main org, but I get an error when I try to validate it.

 

The most frustrating thing is that the validation error is being triggered by some code previously written by a developer - it seems that some validation rules, introduced since the developer wrote his code, are causing the problem.

 

What I don't understand is that I seem to have fixed the problem but am still getting the error message.

 

Here is the error:

	Failure Message: "System.DmlException: Update failed. First exception on row 0 with id 0062000000JZehyAAD; first error: FIELD_CUSTOM_VALIDATION_EXCEPTION, Please enter an email address on the contact page before closing deal.: []", Failure Stack Trace: "Class.TestTriggers.Test_AllTriggers: line 82, column 9 External entry point"

 And here is the test class that seems to be causing the issue:

@IsTest private class TestTriggers {

     static testMethod void Test_AllTriggers(){
    
    //Create an SRT for user 2020 mgt in Aug
        SRT__c srt = New SRT__c(Name='Test SRT',User__c='00520000001BboG',Date__c=System.Today(),New_Business_Target__c=10000,Online_Target__c=1000);
        insert srt;
        
        //Integer lastMonth = Date.month(srt.Date__c)-1;
        Date d = srt.Date__c;//Date.newInstance(Date.year(srt.Date__c), , pInteger)
        d  = d.AddMonths(-1);
        SRT__c srtLastMonth = New SRT__c(Name='Test SRT',User__c='00520000001BboG',Date__c=d,New_Business_Target__c=10000,Online_Target__c=1000);
        insert srtLastMonth;
        
        srt.Last_Month_s_SRT__c=srtLastMonth.id;
        update srt;
        SRT__c srtTest = [Select OwnerId,User__c from SRT__c where Id = :srt.id];
        
                
        //Add an account
        User u = [select id from user where lastname = 'burns' and isactive=true limit 1];
        Account acc = New Account(Name='Adam test',OwnerId=u.id,Billingstreet='asa',BillingPostalCode='test',Billingcity='test',billingcountry='United Kingdom');
        
        Insert acc;
        //Get delegate record type Id
        List<Schema.RecordTypeInfo> contactRecordTypeInfos = Contact.SobjectType.getDescribe().getRecordTypeInfos();
        RecordType rt;
        
        for(Schema.RecordTypeInfo item : contactRecordTypeInfos){
            
            if(item.getName()=='Delegate'){
                rt = New RecordType(id=item.getRecordTypeId()) ;    
            }
        }
        
        //Add a contact
        Contact con = New Contact(AccountID= acc.Id,RecordTYpeId=rt.id,Lastname='Tester',Email='test@test.com');
        insert con;
        String conOwnerId = [select ownerid from contact where id = :con.id].ownerid;
        
        System.debug('Account Owner ID' + acc.OwnerId);
        System.debug('Contact Owner ID' + conOwnerId);
        System.assert(acc.Ownerid <> conOwnerId);
        
        //Add an opportunity
        List<Opportunity> oppArray = New List<Opportunity>();
        oppArray.add(New Opportunity(AccountId=acc.Id,Name='Opp Test',CloseDate=System.today(),Stagename='No ANswer'));
        oppArray.add(New Opportunity(AccountId=acc.Id,Name='Opp Test',CloseDate=System.today(),Stagename='No ANswer'));
        //Opportunity opp = New Opportunity(AccountId=acc.Id,Name='Opp Test',CloseDate=System.today(),Stagename='No ANswer');
        Opportunity opp = New Opportunity(AccountId=acc.Id,Name='Opp Test',CloseDate=System.today(),Stagename='No ANswer');
        insert opp;
        //insert oppArray;
        list<id> oppId = New list<id>();
        
        for(integer i = 0;i<oppArray.size();i++){
            oppId.add(oppArray[i].id);
        }
       System.debug('|||||' + oppId);
        Opportunity oppTest = [select name,deal_ref__c from opportunity where id = :opp.Id];
        //oppTest[0].CDSOpportunity__c =true;
        //oppTest[1].CDSOpportunity__c =true;
        //update oppTest;
        System.debug('@@@@@@' + oppTest);
        System.assert(oppTest.Name == oppTest.Deal_Ref__c);
        
        // Add an item
        
        //create an event/Issue
        Issue_Year__c ei = New Issue_Year__c(Name='Test',Description__c = 'Test Desc',Product__c='CFO',Date__c=System.today(),Mag_or_Event__c='Mag',Online__c='No', Copy_Deadline__c=System.today());
        insert ei;
        List<Schema.RecordTypeInfo> itemRecordTypeInfos = Item__c.SobjectType.getDescribe().getRecordTypeInfos();
        
        for(Schema.RecordTypeInfo item : itemRecordTypeInfos){
            
            if(item.getName()=='Delegate'){
                rt = New RecordType(id=item.getRecordTypeId()) ;    
            }
        }
        opp.StageName = 'Closed - Awaiting Approval';
        opp.Invoice_Address__c = 'Company';
        opp.Address_Invoice_to_contact_above__c = 'yes';
        update opp;
        Item__c item = New Item__c(Payment_terms__c='7 days',Probability__c=100,RecordTYpeId = rt.Id,Attendee__c=con.id,Name='Item Test',Opportunity__c=opp.id,Item_Sold__c='delegate',Event_Issue__c=ei.id);
        insert item;
        
        Item__c itemTest = [Select Line_Number__c from Item__c where id = :item.id];
        System.assert(itemTest.Line_Number__c == 1);
        Integer contactAttendanceCounter = [select count() from ContactAttendance__c where Contact__c = :con.id];
        System.Assert(contactAttendanceCounter==1);
        
        //Change the opportunity to be closed won
        opp.StageName = 'Closed test';
        //opp.Probability = 100;
        Update opp;
        
        opp.StageName = 'Closed Won';      
        Update opp;
        
       item.Overide_Deal_Date__c = d.addYears(2);
       
       update item;
        
        //opp.CDSOpportunity__c = true;
        //update opp;
        opp.StageName = 'Closed test';
        update opp;
        delete item;
        delete opp;
        
        
    }
}

 As you can see, I have set the email address on the contact page here:

        //Add a contact
        Contact con = New Contact(AccountID= acc.Id,RecordTYpeId=rt.id,Lastname='Tester',Email='test@test.com');
        insert con;

So what am I doing wrong?

 

Thanks for your help.

And I though test classes were easy....

 

I am writing a Test Class for a Trigger which updates the count of a child object in the parent object. Except the Test Class does not seem to pass.

 

The parent and child are both Accounts, where the child record is identified by a lookup field to the parent record. When I change an Account to be a child of another Account, the trigger fires an update of the count in the parent record.

 

The test class runs a very simple test, which creates two Accounts (A and B), then updates the lookup in B to point to A. This should cause the Trigger to run, which would update the count on the parent Account, A.

 

However, the test class fails, as the count on the parent is still NULL.

 

I have performed the same test manually and it works - the count on the parent Account is definitely increased.

 

So why does the Test Class fail??

 

Here is the class:

@isTest
private class TestDepartmentCountChange {

static testMethod void testCountChange() {

Account companya = new Account(Name = 'Company A', RecordTypeId = '01220000000Dqq7');
insert companya;

Account companyb = new Account(Name = 'Company B', RecordTypeId = '01220000000Dqq7', Department__c = TRUE, Parent_Company__c = companya.id);
insert companyb;

//system.assert(companya.No_of_Departments__c == NULL);

//companyb.Department__c = TRUE;
//companyb.Parent_Company__c = companya.id;
//update companyb;

system.assert(companya.No_of_Departments__c == 1);

companyb.Department__c = FALSE;
companyb.Parent_Company__c = NULL;
update companyb;

system.assert(companya.No_of_Departments__c == NULL);

}
}

 And here is the Trigger:

trigger DepartmentSumTrigger2 on Account (after delete, after insert, after undelete,
after update) {

    // When deleting a Department use the previous values
    Account[] dept;
    if (Trigger.isDelete){
        dept = Trigger.old;
    }
  
    else
        dept = Trigger.new;
    

   
    // Get a list of all Accounts that are Parent Companies
    Set<ID> acctIds = new Set<ID>();
    for (Account dep :dept) {
        acctIds.add(dep.Parent_Company__c);
    }
    
    // Fill a Map with a list of all Departments
    Map<ID, Account> departmentsForAccounts = 
    new Map<ID, Account>([SELECT Id, Parent_Company__c FROM Account WHERE Department__c = TRUE AND Parent_Company__c IN :acctIds]);
    
    // Fill a map with a list of all Parent Companies
    Map<ID, Account> acctsToUpdate = 
    new Map<ID, Account>([SELECT Id, No_of_Departments__c FROM Account WHERE Id IN :acctIds]);
    
    // For all Parent Companies            
    for (Account acct : acctsToUpdate.values()) {
        Set<ID> depIds = new Set<ID>();
        for (Account dep : departmentsForAccounts.values()) {
            if (dep.Parent_Company__c == acct.Id)
                depIds.add(dep.Id);
        }
        if (acct.No_of_Departments__c != depIds.size())
            acct.No_of_Departments__c = depIds.size();
    }

    update acctsToUpdate.values();

}



I am fairly new to Apex, and I'm trying to update a list of Contacts whenever something is changed in the parent Account. The Trigger I have written works, apart from where the Contact Owner is Inactive.

 

I could transfer the Contacts to another, active Owner and update them. But for Opportunities, I need to put the old Owner back. So my idea is to activate the Owner, update the Contact, then deactivate the Owner.

 

However, everything I try generates the Mixed DML Operation error, because I am trying to update both the Contact and User objects. I have researched this problem and found the following. It mentions that I can use the @future method inside a class, then call the class from the trigger.

 

However, I don't know where to start with this. My experience with classes is around test classes - so I'm not sure how to use a class to actually update a record permanently.

 

This is the code I have to update the Contacts:

trigger UpdateChildObjectsToDepartment on Account (after update) {

  Map<Id, Account> accountMap = new Map<Id,Account>();
  Account parent;

//For changing company TO Deparment
  for (Integer i = 0; i < Trigger.new.size(); i++){
    if (Trigger.new[i].Department__c == true && Trigger.old[i].Department__c == false) {
      accountMap.put( Trigger.new[i].Id, Trigger.new[i]);
    }
  }
  
 
  if( accountMap.size() > 0 ) {
  Set<Id> accountIds = accountMap.keySet();
  
  List<Contact> relatedContacts =
      [SELECT f.id, f.Accountid,  f.Owner.Name, f.Department__r.id, f.Ownerid, f.Owner.IsActive
      FROM Contact f
      WHERE f.Accountid IN :accountIds
      FOR UPDATE];
      
  for ( Contact child : relatedContacts ) {
        if ( accountMap.containsKey( child.Accountid ) ) {
        parent = accountMap.get( child.Accountid );
        child.Department__c = child.Accountid;
        child.Accountid = parent.Parent_Company__c;
        }
      }
      
      
update relatedContacts;
   
}}

 

I am fairly new to Apex classes and triggers, but I am finding my feet with the code I have recently written.

 

Having written and tested the code I am now trying to deploy it in the main org, but I get an error when I try to validate it.

 

The most frustrating thing is that the validation error is being triggered by some code previously written by a developer - it seems that some validation rules, introduced since the developer wrote his code, are causing the problem.

 

What I don't understand is that I seem to have fixed the problem but am still getting the error message.

 

Here is the error:

	Failure Message: "System.DmlException: Update failed. First exception on row 0 with id 0062000000JZehyAAD; first error: FIELD_CUSTOM_VALIDATION_EXCEPTION, Please enter an email address on the contact page before closing deal.: []", Failure Stack Trace: "Class.TestTriggers.Test_AllTriggers: line 82, column 9 External entry point"

 And here is the test class that seems to be causing the issue:

@IsTest private class TestTriggers {

     static testMethod void Test_AllTriggers(){
    
    //Create an SRT for user 2020 mgt in Aug
        SRT__c srt = New SRT__c(Name='Test SRT',User__c='00520000001BboG',Date__c=System.Today(),New_Business_Target__c=10000,Online_Target__c=1000);
        insert srt;
        
        //Integer lastMonth = Date.month(srt.Date__c)-1;
        Date d = srt.Date__c;//Date.newInstance(Date.year(srt.Date__c), , pInteger)
        d  = d.AddMonths(-1);
        SRT__c srtLastMonth = New SRT__c(Name='Test SRT',User__c='00520000001BboG',Date__c=d,New_Business_Target__c=10000,Online_Target__c=1000);
        insert srtLastMonth;
        
        srt.Last_Month_s_SRT__c=srtLastMonth.id;
        update srt;
        SRT__c srtTest = [Select OwnerId,User__c from SRT__c where Id = :srt.id];
        
                
        //Add an account
        User u = [select id from user where lastname = 'burns' and isactive=true limit 1];
        Account acc = New Account(Name='Adam test',OwnerId=u.id,Billingstreet='asa',BillingPostalCode='test',Billingcity='test',billingcountry='United Kingdom');
        
        Insert acc;
        //Get delegate record type Id
        List<Schema.RecordTypeInfo> contactRecordTypeInfos = Contact.SobjectType.getDescribe().getRecordTypeInfos();
        RecordType rt;
        
        for(Schema.RecordTypeInfo item : contactRecordTypeInfos){
            
            if(item.getName()=='Delegate'){
                rt = New RecordType(id=item.getRecordTypeId()) ;    
            }
        }
        
        //Add a contact
        Contact con = New Contact(AccountID= acc.Id,RecordTYpeId=rt.id,Lastname='Tester',Email='test@test.com');
        insert con;
        String conOwnerId = [select ownerid from contact where id = :con.id].ownerid;
        
        System.debug('Account Owner ID' + acc.OwnerId);
        System.debug('Contact Owner ID' + conOwnerId);
        System.assert(acc.Ownerid <> conOwnerId);
        
        //Add an opportunity
        List<Opportunity> oppArray = New List<Opportunity>();
        oppArray.add(New Opportunity(AccountId=acc.Id,Name='Opp Test',CloseDate=System.today(),Stagename='No ANswer'));
        oppArray.add(New Opportunity(AccountId=acc.Id,Name='Opp Test',CloseDate=System.today(),Stagename='No ANswer'));
        //Opportunity opp = New Opportunity(AccountId=acc.Id,Name='Opp Test',CloseDate=System.today(),Stagename='No ANswer');
        Opportunity opp = New Opportunity(AccountId=acc.Id,Name='Opp Test',CloseDate=System.today(),Stagename='No ANswer');
        insert opp;
        //insert oppArray;
        list<id> oppId = New list<id>();
        
        for(integer i = 0;i<oppArray.size();i++){
            oppId.add(oppArray[i].id);
        }
       System.debug('|||||' + oppId);
        Opportunity oppTest = [select name,deal_ref__c from opportunity where id = :opp.Id];
        //oppTest[0].CDSOpportunity__c =true;
        //oppTest[1].CDSOpportunity__c =true;
        //update oppTest;
        System.debug('@@@@@@' + oppTest);
        System.assert(oppTest.Name == oppTest.Deal_Ref__c);
        
        // Add an item
        
        //create an event/Issue
        Issue_Year__c ei = New Issue_Year__c(Name='Test',Description__c = 'Test Desc',Product__c='CFO',Date__c=System.today(),Mag_or_Event__c='Mag',Online__c='No', Copy_Deadline__c=System.today());
        insert ei;
        List<Schema.RecordTypeInfo> itemRecordTypeInfos = Item__c.SobjectType.getDescribe().getRecordTypeInfos();
        
        for(Schema.RecordTypeInfo item : itemRecordTypeInfos){
            
            if(item.getName()=='Delegate'){
                rt = New RecordType(id=item.getRecordTypeId()) ;    
            }
        }
        opp.StageName = 'Closed - Awaiting Approval';
        opp.Invoice_Address__c = 'Company';
        opp.Address_Invoice_to_contact_above__c = 'yes';
        update opp;
        Item__c item = New Item__c(Payment_terms__c='7 days',Probability__c=100,RecordTYpeId = rt.Id,Attendee__c=con.id,Name='Item Test',Opportunity__c=opp.id,Item_Sold__c='delegate',Event_Issue__c=ei.id);
        insert item;
        
        Item__c itemTest = [Select Line_Number__c from Item__c where id = :item.id];
        System.assert(itemTest.Line_Number__c == 1);
        Integer contactAttendanceCounter = [select count() from ContactAttendance__c where Contact__c = :con.id];
        System.Assert(contactAttendanceCounter==1);
        
        //Change the opportunity to be closed won
        opp.StageName = 'Closed test';
        //opp.Probability = 100;
        Update opp;
        
        opp.StageName = 'Closed Won';      
        Update opp;
        
       item.Overide_Deal_Date__c = d.addYears(2);
       
       update item;
        
        //opp.CDSOpportunity__c = true;
        //update opp;
        opp.StageName = 'Closed test';
        update opp;
        delete item;
        delete opp;
        
        
    }
}

 As you can see, I have set the email address on the contact page here:

        //Add a contact
        Contact con = New Contact(AccountID= acc.Id,RecordTYpeId=rt.id,Lastname='Tester',Email='test@test.com');
        insert con;

So what am I doing wrong?

 

Thanks for your help.