I have written a trigger and handler class (which uses a future method) and it works well. I am really struggling to write the test class for it. My first problem is that the code coverage is high (before tweaks it was 0% in the handler class). After tweaking, I am getting the following error:
System.JSONException: Malformed JSON: Expected '[' at the beginning of List/Set
I am not sure how to solve this and I am a pretty new dev so not sure what to do next . Can someone please help?
Here is my code:
Trigger:
trigger AccountTriggerKeepAcctTeam on Account (before update) {
List<AccountTeamMember> listAcc = [SELECT Id, AccountAccessLevel, AccountId, CaseAccessLevel, UserId, ContactAccessLevel, OpportunityAccessLevel, TeamMemberRole, PhotoUrl, Title FROM AccountTeamMember Where AccountId in : Trigger.new AND TeamMemberRole != 'Account Executive'];
for(Account acc: Trigger.new){
Account oldAccount = Trigger.oldMap.get(acc.Id);
if(acc.OwnerId != oldAccount.OwnerId){
system.debug('AccountTeamMember records: '+(JSON.serialize(listAcc)));
String str = JSON.serialize(listAcc);
//delete team member records if required
AccountTriggerKeepAcctTeamHandler.retainOldTeamMemberOnOwnerChange(str);
}
}
}
Handler:
public class AccountTriggerKeepAcctTeamHandler {
#future
public static void retainOldTeamMemberOnOwnerChange(String str){
system.debug('Future call '+str);
List<AccountTeamMember> newlistAcc = (List<AccountTeamMember>) JSON.deserialize(str,List<AccountTeamMember>.class);
for(AccountTeamMember objAccTeamMember : newlistAcc){
objAccTeamMember.Id= null;
}
system.debug('Account records to insert'+(JSON.serialize(newlistAcc)));
Upsert newlistAcc;
}
}
Test class:
#isTest
public class AccountTriggerKeepAcctTeamTest {
//#TestSetup
static testMethod void AcctOwnerChange(){
List<User> userList = TestDataFactory.createUser(true, 2);
Profile p = [SELECT Id FROM Profile WHERE Name='System Administrator'];
User u1 = new User(Alias = 'standt', Email='standarduser#testorg1.com',
EmailEncodingKey='UTF-8', LastName='Testing', LanguageLocaleKey='en_US',
LocaleSidKey='en_US', ProfileId = p.Id,
TimeZoneSidKey='America/Los_Angeles', UserName='standarduser#testorg01.com');
insert u1;
User u2 = new User(Alias = 'standt', Email='standarduser#testorg2.com',
EmailEncodingKey='UTF-8', LastName='Testing2', LanguageLocaleKey='en_US',
LocaleSidKey='en_US', ProfileId = p.Id,
TimeZoneSidKey='America/Los_Angeles', UserName='standarduser#testorg92.com');
insert u2;
System.runAs(u1){
Allowed_Account_Owner_Change__c setting = new Allowed_Account_Owner_Change__c();
setting.Allowed_to_change_Account_Owner__c = true;
insert setting;
fferpcore__ExchangeRateGroup__c exrg = new fferpcore__ExchangeRateGroup__c(CurrencyISOCode = 'USD', fferpcore__DefaultCurrency__c = 'USD - U.S. Dollar', Name = 'FF Shared Test Group', fferpcore__SelectedCurrencies__c = 'USD - U.S. Dollar');
insert exrg;
c2g__codaCompany__c company = new c2g__codaCompany__c();
company.Name = 'ApexTestCompany';
company.RecordTypeId = Schema.SObjectType.c2g__codaCompany__c.RecordTypeInfosByName.get('SUT').RecordTypeId;
insert company;
company.c2g__ExchangeRateGroup__c = exrg.Id;
update company;
Account acc = new Account(Name = 'Test Acc2', NumberOfEmployees = 500);//TestDataFactory.createAccountwithCurrencyMaster(true);
acc.OwnerId = userinfo.getUserId();
insert acc;
AccountTeamMember accTeam = new AccountTeamMember();
accTeam.UserId = acc.OwnerId;
accTeam.AccountAccessLevel = 'Read';
accTeam.AccountId = acc.Id;
insert accTeam;
System.debug('## User '+userinfo.getUserName());
//create opportunity
//}
// }
// static testMethod void testAcctOwnerChange(){
// User u = [Select id, LastName from User where LastName = 'Testing2'];
// Account acc = [Select id, OwnerId from Account Limit 1];
Test.startTest();
acc.OwnerId = u2.id;
update acc;
AccountTriggerKeepAcctTeamHandler.retainOldTeamMemberOnOwnerChange(JSON.serialize(acc));
//System.assertEquals(accTeam.UserId,u2.Id);
Test.stopTest();
//AccountTeamMember atm = [Select userId,AccountId, AccountAccessLevel from AccountTeamMember where AccountId =: acc.Id];
}
}
Can someone please help? I am a beginner apex dev
Trigger Attempt for Alin's test class:
trigger AccountTriggerKeepAcctTeam on Account (before update) {
Set<Id> accteammbrId = new Set<Id>();
List<AccountTeamMember> listAcc = [SELECT Id, AccountAccessLevel, AccountId, CaseAccessLevel, UserId, ContactAccessLevel, OpportunityAccessLevel, TeamMemberRole, PhotoUrl, Title FROM AccountTeamMember Where AccountId in : Trigger.new AND TeamMemberRole != 'Account Executive'];
for(Account acc: Trigger.new){
Account oldAccount = Trigger.oldMap.get(acc.Id);
if(acc.OwnerId != oldAccount.OwnerId){
//system.debug('AccountTeamMember records: '+(JSON.serialize(listAcc)));
//String str = JSON.serialize(listAcc);
for(AccountTeamMember actmbr : listAcc){
accteammbrId.add(actmbr.Id);
}
//delete team member records if required
AccountTriggerKeepAcctTeamHandler.retainOldTeamMemberOnOwnerChange(accteammbrId);
}
}
}
public class AccountTriggerKeepAcctTeamHandler {
#future
public static void retainOldTeamMemberOnOwnerChange(Set<Id> AccteambrId){
List<AccountTeamMember> newlistAcc = [SELECT Id, AccountAccessLevel, AccountId, CaseAccessLevel, UserId, ContactAccessLevel, OpportunityAccessLevel, TeamMemberRole, PhotoUrl, Title FROM AccountTeamMember Where AccountId in :AccteambrId];
//system.debug('Future call '+str);
//List<AccountTeamMember> newlistAcc = (List<AccountTeamMember>) JSON.deserialize(str,List<AccountTeamMember>.class);
for(AccountTeamMember objAccTeamMember : newlistAcc){
objAccTeamMember.Id= null;
}
//system.debug('Account records to insert'+(JSON.serialize(newlistAcc)));
system.debug('Account records to insert' + newlistAcc);
Upsert newlistAcc;
}
}
I don't understand why you have to serialize the list in the first place.
Change your retainOldTeamMemberOnOwnerChange to accept a List as parameter and don't use the JSON at all.
Then in your test class you create an account, assign an account team to it and change the owner. After that you will query the List of associated AccountTeamMember and assert it with regards to the AccountTeamMember before the ownership of the account was changed.
If it's not clear enough I can give you a code example, but I think the exercise will be good for you.
Edit: I see. My bad. This is what I use for test coverage. I can also provide code sample from the trigger if needed.
Trigger:
trigger AccountTrigger on Account (before update, After Update) {
if(trigger.isBefore){
if(trigger.isUpdate){
AccountTriggerHandler.retainAccountTeam(trigger.new, trigger.oldMap);
}
}
}
Handler:
public class AccountTriggerHandler {
public static void retainAccountTeam(List<Account> newList, Map<Id, Account> oldMap) {
Set<Id> accountsWithNewOwners = new Set<Id>();
for(Account acc : newList){
if(acc.OwnerId != oldMap.get(acc.Id).OwnerId){
accountsWithNewOwners.add(acc.Id);
}
}
if(accountsWithNewOwners.size() > 0){
List<AccountTeamMember> listAcc = [SELECT Id, AccountAccessLevel, AccountId, CaseAccessLevel, UserId, ContactAccessLevel, OpportunityAccessLevel, TeamMemberRole, PhotoUrl, Title FROM AccountTeamMember WHERE AccountId IN :accountsWithNewOwners];
system.debug('AccountTeamMember records: '+(JSON.serialize(listAcc)));
String str = JSON.serialize(listAcc);
// delete team member records if required
retainOldTeamMemberOnOwnerChange(str);
}
}
//Before Update calls the future method that basically reinserts the Account Team member records. You can also consider deleting the Account Team Member records before calling the future method.
#future
public static void retainOldTeamMemberOnOwnerChange(String str){
system.debug('Future call '+str);
List<AccountTeamMember> newlistAcc = (List<AccountTeamMember>) JSON.deserialize(str,List<AccountTeamMember>.class);
for(AccountTeamMember objAccTeamMember : newlistAcc){
objAccTeamMember.Id= null;
}
system.debug('Account records to insert'+(JSON.serialize(newlistAcc)));
Upsert newlistAcc;
}
}
Test method:
static testMethod void test_retainAccountTeam(){
Id mhbkRTId = Schema.SObjectType.Account.getRecordTypeInfosByName().get('MHBK').getRecordTypeId();
Profile p = [SELECT Id FROM Profile WHERE Name='System Administrator'];
User u = new User(
Alias = 'rivacon',
Email = 'rivaConnector#testorg.com',
EmailEncodingKey = 'UTF-8',
LastName = 'Connector',
FirstName = 'Riva',
LanguageLocaleKey = 'en_US',
LocaleSidKey = 'en_US',
ProfileId = p.Id,
TimeZoneSidKey = 'America/Los_Angeles',
UserName = 'rivaConnector#testorg.com');
insert u;
User u2 = new User(
Alias = 'updated',
Email = 'rivaConnector#testorg.com',
EmailEncodingKey = 'UTF-8',
LastName = 'Connector',
FirstName = 'Riva2',
LanguageLocaleKey = 'en_US',
LocaleSidKey = 'en_US',
ProfileId = p.Id,
TimeZoneSidKey = 'America/Los_Angeles',
UserName = 'rivaConnector2#testorg.com');
insert u2;
system.runAs(u){
Account acc = new Account(RecordTypeId = mhbkRTId,Name = 'test',Branch__c='London',Industry='Banking',Year_Founded__c='1900',BillingStreet = '1 Long Rd',BillingCity = 'City',CurrencyIsoCode = 'GBP');
insert acc;
AccountTeamMember atm = new AccountTeamMember();
atm.AccountId = acc.Id;
//atm.Name ='Test ATM';
atm.TeamMemberRole = 'Sales Assistant';
atm.UserId = u.Id;
insert atm;
Test.startTest();
acc.OwnerId = u2.Id;
update acc;
Test.stopTest();
System.debug('usr1 ' + u.id);
System.debug('usr2 ' + u2.id);
Account updatedAccount = [SELECT OwnerId FROM Account WHERE Id = :acc.Id];
system.assertEquals(u2.Id, updatedAccount.OwnerId);
List<AccountTeamMember> teamMembers = [SELECT Id, AccountAccessLevel, AccountId, UserId, TeamMemberRole, Title FROM AccountTeamMember WHERE AccountId = :acc.Id];
system.assertEquals(1, teamMembers.size());
system.assertEquals(teamMembers[0].TeamMemberRole, atm.TeamMemberRole);
}
1I am working with controller code in MVC.
I have the follow code working where the are related entities priority list based on teams:
public JsonResult GetPrioritiesByTeam(int id)
{
List<Priority> priorities = new List<Priority>();
if (id > 0)
{
priorities = db.Priorities.Where(p => p.Team == id ).ToList();
}
else
{
priorities.Insert(0, new Priority { Id = 0, PriorityDescription = "--Select a Team first--" });
}
var result = (from r in priorities
select new
{
id = r.Id,
name = r.PriorityDescription
}).ToList();
return Json(result, JsonRequestBehavior.AllowGet);
}
The statement
priorities = db.Priorities.Where(p => p.Team == id
does get me the list of priorities based on the team passed in by the parameter id.
I am now try to achieve the same successful result with another entity that is related to team- an entity called 'members'. However the relationship is not a one to many. Its a many to many- so I have an intermediate entity call 'teammembers'.
The Team is still the entity driving the choice. When the team is chosen, I need it to give me all the team member entries in teammembers, and then I need similar to the priorities code above, give me a members list which is based on the teammembers identified based on the team selected.
I think based on the code provided the else clause would be similar coding along with the var result coding.
I just cant seem to get the coding right for the clause in the condition for id >0.
How do I write that clause for the many to many so as to get the list of members for the team through teammembers?
You can assume entity team is Id and TeamDescription, entity Member is Id and MemberName, and entity teammember is an ID,and teamId and MemberId -just a straightforward many to many.
Been trying with LINQ method syntax and LINQ query syntax but cant get it working.
Thanks for any help you can provide
New code would look like the following except i need working id>0 clause:
public JsonResult GetMembersByTeam(int id)
{
List<Member> members = new List<Member>();
if (id > 0)
{
**members = Members.Where(needed clause).ToList();**
}
else
{
members.Insert(0, new Member { Id = 0, MemberFullName = "--Select a Team first--" });
}
var result = (from m in members
select new
{
id = m.Id,
name = m.MemberFullName
}).ToList();
return Json(result, JsonRequestBehavior.AllowGet);
}
Entity Diagram
Current code with suggested changes
public JsonResult GetMembersByTeam(int id)
{
List<Member> members = new List<Member>();
if (id > 0)
{
//members = db.Teams.Where(p => p.Id == id)
// .SelectMany(e => e.TeamMembers)
// .Select(e => e.Member)
// .ToList();
members = db.TeamMembers.Where(p => p.Team == id)
.Select(e => e.Member)
.ToList();
}
else
{
members.Insert(0, new Member { Id = 0, MemberFullName = "--Select a Team first--" });
}
var result = (from m in members
select new
{
id = m.Id,
name = m.MemberFullName
}).ToList();
return Json(result, JsonRequestBehavior.AllowGet);
}
results in error :
Cannot implicitly convert type 'System.Collections.Generic.List' to 'System.Collections.Generic.List,ManageHR5.model.Member>'
try this:
members = db.TeamMembers.Where(p => p.Team == id )
.Select(e => e.Member1)
.ToList();
I am trying to create salesforce trigger on Lead that auto-populates a look up field which links the current Lead to an existing Account if there exist an Account with the same name as the Lead's Company custom field.
This is my code:
trigger Link_Lead_To_Account on Lead (before insert ) {
Set<String> whatIDs = new Set<String>();
MAP<id,String> accountMap= new MAP<id,String>();
// save the leads that have been triggered
for (Lead l : Trigger.new) {
whatIDs.add(l.id);
}
List<Lead> leads = [SELECT Id,Company FROM Lead where ID=:whatIDs ];
// loop through the triggered leads, if the account.name == to lead.company then link the found account to the lead
for (Integer i = 0; i <Trigger.new.size(); i++)
{
// System.Debug('++++++++++++++'+Trigger.new[i].company+Trigger.new[i].id);
if(accountMap.get(Trigger.new[i].company)!=null)
{
for(Account ac :[Select name,id from Account])
{
if(Trigger.new[i].Company==ac.Name)
{
Trigger.new[i].Account__c= ac.id;
break;
}
}
}
// System.Debug('Trigger.new[i].Account__c::::'+Trigger.new[i].Account__c);
// System.Debug('Trigger.new[i].company:::::'+Trigger.new[i].company);
// System.Debug('Trigger.new[i].ID:::::'+Trigger.new[i].ID);
}
update leads;
}
But it doesn't work at all. It throws the following error:
Review all error messages below to correct your data.
Apex trigger Link_Lead_To_Account caused an unexpected exception, contact your administrator: Link_Lead_To_Account: execution of AfterInsert caused by: System.StringException: Invalid id: TestAccount2: External entry point
As it requires the Company field to be an ID, but when I write an ID it does't perform any changes.
I managed to fix it.
This is the working class where newLeads.Values() is being populated in the constructor with to the Trigger.new() values on the before insert event:
public void LinkLeadToAccount() {
Set<String> companies = new Set<String>();
for (Lead l: newLeads.values()) {
if (l.Company != null) companies.add(l.Company);
}
if (companies.size() > 0) {
// Pick most recent Account where more than one with same name
Map<String, Id> accountNameToId = new Map<String, Id>();
for (Account a : [
select Name, Id
from Account
where Name in :companies
order by CreatedDate
]) {
accountNameToId.put(a.Name, a.Id);
}
if (accountNameToId.size() > 0) {
Lead[] updates = new Lead[] {};
for (Lead l: newLeads.values()) {
if (l.Company != null) {
Id accountId = accountNameToId.get(l.Company);
if (accountId != null) {
updates.add(new Lead(Id = l.Id, Account__c = accountId));
}
}
}
System.debug(' leads_to_update : ' + updates.size() + ' leads_to_update : ' + updates);
update updates;
}
}
}
I have made a trigger to update the Contact ID in person account and its working for person accounts it is populating the Contact ID field on person account.
The problem is it is giving an error on business and employer account record type i have also inserted a check for record type. But still its giving an error
on Error: Invalid Data.
Review all error messages below to correct your data.
Apex trigger UpdateContactID caused an unexpected exception, contact your administrator: UpdateContactID: execution of AfterInsert caused by: System.NullPointerException: Attempt to de-reference a null object: Trigger.UpdateContactID: line 41, column 1
The trigger is below:
trigger UpdateContactID on Account (after insert, after update)
{
//Identify applicable record type
RecordType PersonAccount = [SELECT Id FROM RecordType WHERE SobjectType='Account' AND Name = 'Career Champion Account' limit 1];
List<Account> toUpdate = new List<Account>();
if(trigger.isInsert)
{
List<String> newAccountIDList = new List<String>();
// Taking all account IDs in collection
for(Account acct: Trigger.new)
{
newAccountIDList.add(acct.ID);
}
// Fetching contacts against the account IDs
List<Contact> contactList = [SELECT Id, Account.Id FROM Contact WHERE Account.ID in :newAccountIDList];
// Adding contacts in a map with relation to Account ID
Map<String, Contact> mapContact = new Map<String, Contact>();
for(Contact cont : contactList)
{
mapContact.put(cont.Account.Id, cont) ;
}
// Updating Contact_ID__c from Map to new Account list to update
List<Account> newAccounts = [select Id, Contact_ID__c from Account where Id in :newAccountIDList];
for(Account acct: newAccounts)
{
**LINE 41**
toUpdate.add(new Account(
id = acct.Id,
Contact_ID__c = mapContact.get(acct.Id).Id
));
}
update toUpdate;
} // if
else if(trigger.isUpdate && trigger.new[0].Contact_ID__c == null && trigger.old[0].Contact_ID__c == null)
{
List<String> newAccountIDList = new List<String>();
// Taking all account IDs in collection
for(Account acct: Trigger.new)
{
newAccountIDList.add(acct.ID);
}
// Fetching contacts against the account IDs
List<Contact> contactList = [SELECT Id, Account.Id FROM Contact WHERE Account.ID in :newAccountIDList];
// Adding contacts in a map with relation to Account ID
Map<String, Contact> mapContact = new Map<String, Contact>();
for(Contact cont : contactList)
{
mapContact.put(cont.Account.Id, cont) ;
}
// Updating Contact_ID__c from Map to new Account list to update
List<Account> newAccounts = [select Id, Contact_ID__c from Account where Id in :newAccountIDList];
// List<Account> toUpdate = new List<Account>();
for(Account acct: newAccounts)
{
toUpdate.add(new Account(
id = acct.Id,
Contact_ID__c = mapContact.get(acct.Id).Id
));
}
update toUpdate;
} // else if
} // trigger
Most likely mapContact does not contain a value for the key acct.Id. The mapContact.get(acct.Id) call will return null and the following .Id will give the null reference exception.
You should add a guard statement before that line. E.g.
if(mapContact.containsKey(acct.Id)) {
toUpdate.add(new Account(
id = acct.Id,
Contact_ID__c = mapContact.get(acct.Id).Id
));
}
Newbie here, I have a problem with my code. Before this code returns an array of string (student name) but I need to add the student id without requiring me to recode the entire method. Can someone help me on how to do it? Basically I need to include the Student ID based on the Student Name.
TIA.
public Student[] GetAllStudents(string subject)
{
Student[] students = cache.GetAllStudents(subject);
if (students == null)
{
Subjects group = RetrieveSubjects(subject);
if (group != null)
{
students = group.Students.Select(r => r.StudentName).ToArray();
// I need to include also the Student ID based on the Student Name queried above.
}
else
{
students = new string[0];
}
cache.AddAllStudents(subject, students);
}
return students;
}
You can use anonymous class in your select clause, ie
students = group.Students
.Select(r => new { name= r.StudentName, id= r.StudentID } )
.ToArray();
Or just select Student object as it should has all you need
students = group.Students.ToArray();
// doing .Select(r=>r) is redundant and can be omited
Seem that you need to create Student class from your studentinfo class, you would want to change prop's in initializer.
students = group.Students
.Select(r => new Student {
StudentName= r.StudentName,
StudentID= r.StudentID
})
.ToArray();