salesforce update trigger on serviceterritory object - salesforce

I have below requirement and I'm new to SF development, need assistance if my approach is correct or not.
When any update happens on ServiceTerritory object, if the Time_Zone__c field of ServiceTerritory is not matching with User Object TimeZoneSidKey field, then update ServiceTerritory object Time_Zone__c field with User Object TimeZoneSidKey field.
ServiceTerritory object : has Center_Instructor_Contact__c field tagged to ID field in Contact object.
Contact object : has ID field and AccountId field
User Object : has AccountId field
public static void afterUpdate(List<ServiceTerritory> serviceTerritories, Map<Id, ServiceTerritory> oldRecords) {
Set<Id> recIds = new Set<Id>();
for (ServiceTerritory record : serviceTerritories) {
recIds.add(record.Id);
}
Set<Id> STMembers = new Set<Id>();
for (ServiceTerritory member : [SELECT Id, Center_Instructor_Contact__c FROM ServiceTerritory WHERE Id IN :recIds]) {
STMembers.add(member.Center_Instructor_Contact__c);
}
//Contact object : has ID field and AccountId field
Set<Id> ContactIDs = new Set<Id>();
for (Contact Cnt : [SELECT AccountId FROM Contact WHERE Id IN :STMembers]) {
ContactIDs.add(Cnt.AccountId);
}
//User Object : has AccountId field
Set<Id> UserIDs = new Set<Id>();
for (User Cnt : [SELECT AccountId, TimeZoneSidKey FROM User WHERE AccountId IN :ContactIDs]) {
UserIDs.add(Cnt.AccountId);
}
}
and here how to compare and update ServiceTerritory object if the timezone is not matching between the objects.

Not many Salesforce instances will have access to "Field Service Lightning" which is where ServiceTerritory table is used. If you sign up for free Salesforce Developer Edition it probably won't exist there. So it's bit hard to understand the question and help. Plus if you wrote "User Object : has AccountId field" it sounds like you're using Experience Cloud (formerly known as communities), that narrows down the specialists even more.
So it's Service Territory -> "up" to -> Contact -> "down" to (community) -> User?
If you're using community users they'll have AccountId and ContactId in them, no need going via Account (and in fact you could get stupid results that way... What if not all contacts in account are community-enabled, what if they are but have different timezones...)
Try something like that but you'll have to experiment a lot. And change your code to run "before update", you'll get save to database for free
List<ServiceTerritory> serviceTerritories; // passed to your function
Set<Id> contactIds = new Set<Id>();
for (ServiceTerritory st : serviceTerritories) {
contactIds.add(st.Center_Instructor_Contact__c);
}
System.debug(contactIds);
// Grab all these Contacts and their community users (it's a related list so it'll be a subquery but really there will be at most one user
Map<Id, Contact> contacts = new Map<Id, Contact>([SELECT Id,
(SELECT TimezoneSidKey FROM Users)
FROM Contact
WHERE Id IN :contactIds AND Id IN (SELECT ContactId FROM User)]);
// Loop again and check against "reference data"
for (ServiceTerritory st : serviceTerritories) {
if(contacts.containsKey(st.Center_Instructor_Contact__c)){
Contact c = contacts.get(st.Center_Instructor_Contact__c);
System.debug(c);
System.debug('comparing ' + st.Time_Zone__c + ' and ' + c.Users);
if(st.Time_Zone__c != c.Users[0].TimezoneSidKey){
System.debug('fixing ' + st.Id);
st.Time_Zone__c = c.Users[0].TimezoneSidKey;
}
}
}

Related

Apex trigger to display the sum of value from child to parent account

I have created a trigger on contact object that suppose to sum the amount value from the contact salary fields of the contact object and display the sum in their parent account object Contacts salary. but nothing is getting populated can someone help
When AFTER_INSERT {
for (Contact con : Trigger.new) {
if (String.isBlank(con.AccountId)) {
//Write automation logic here
String accountId = con.AccountId;
List<AggregateResult> results = [Select AccountId, COUNT(Id) ,Sum(Contact_Salary__c) totalSalary FROM CONTACT WHERE AccountId = :accountId GROUP BY AccountID];
for (AggregateResult result : results) {
String accId = String.valueOf(result.get('AccountId'));
System.debug(accId);
Integer totalSalary = Integer.valueOf(result.get('totalSalary'));
Account acc = new Account(Id=accId, Contacts_Salary__c = totalSalary);
update acc;
}
}
}
}
I see that you have given the wrong if check -
if (String.isBlank(con.AccountId)) {
use below one
if(String.isNotBlank(con.AccountId)) {
Also, I suggest please optimize your code as it is not Bulkified, as you are doing DMLs in for loop, that will cause you problem in large amount of data and you will hit the org's Governor limit.
Thanks.

How To Understand #TestClass Apex code with List embedded in Map

I am trying to understand this code ,this seems to be a test class,but i am having hard time to understand the code,i know conceptually how List and Map collection works in Sales force,but this seems to be little difficult to understand,
.In brief to me this seems to test a method which browses a list of CollaborationGroupMember.
For that, CollaborationGroup has been created and code tried to add one User.
can some one please take some time to make me understand the below code line by line?
Thanks in advance
#isTest
public class TestGroupFactory {
public static Map<CollaborationGroup, List<CollaborationGroupMember>> groupWithMember() {
CollaborationGroup groupe = new CollaborationGroup(Name = 'Test1', CollaborationType = 'Public');
insert groupe;
groupe = [SELECT Id, Name FROM CollaborationGroup WHERE Name = 'Test1'];
List<User> users = [SELECT Id, Name, Numero_de_plaque__c, SenderEmail
FROM User
WHERE Name = 'User User'];
List<CollaborationGroupMember> cgms = new List<CollaborationGroupMember>();
for (User u : users) {
CollaborationGroupMember cgm = new CollaborationGroupMember();
cgm.CollaborationGroupId = groupe.Id;
cgm.MemberId = u.Id;
cgms.add(cgm);
}
insert cgms;
return new Map<CollaborationGroup, List<CollaborationGroupMember>>{groupe => cgms};
}
}
It is technically a test class, but it does not perform any tests. Its purpose is to create test data for other test classes that contain test methods. The reason it has the #isTest annotation is so that it is only accessible in test context and does not count against the total test coverage of the organization.
The method shown creates a Chatter Group and adds Users to the group if they have the name "User User".
The code below inserts the Chatter Group and then retrieves it so the Id is available. I don't think the retrieval is necessary in this instance, but I'd have to test it.
CollaborationGroup groupe = new CollaborationGroup(Name = 'Test1', CollaborationType = 'Public');
insert groupe;
groupe = [SELECT Id, Name FROM CollaborationGroup WHERE Name = 'Test1'];
The next section retrieves the Users (presumably created in another test class)
List<User> users = [SELECT Id, Name, Numero_de_plaque__c, SenderEmail
FROM User
WHERE Name = 'User User'];
Then, a list of CollaborationGroupMembers is instantiated. A loop begins that iterates over every User. For each user, a new CollaborationGroupMember is instantiated and added to the list.
List<CollaborationGroupMember> cgms = new List<CollaborationGroupMember>();
for (User u : users) {
CollaborationGroupMember cgm = new CollaborationGroupMember();
cgm.CollaborationGroupId = groupe.Id;
cgm.MemberId = u.Id;
cgms.add(cgm);
}
The group members are inserted
insert cgms;
The group and group members are added to a map and returned
return new Map<CollaborationGroup, List<CollaborationGroupMember>>{groupe => cgms};

Test Class in Salesforce for a Trigger

Good day Everybody,
I am attempting to write a test class for a trigger I helped write. The trigger uses a field called trigger_help__c, a formula field derived from adding the opportunity Type and Account ID, and fires before insert if an Opportunity of that type has been created on that account within the last 90 days. Unless the profile is a system admin. Here is my trigger:
trigger leadDuplicatePreventer on opportunity(before insert) {
set<string> settgs = new set<string>();
list<opportunity> opps = [select id,Trigger_Help__c from opportunity WHERE CreatedDate = LAST_N_DAYS:90];
Profile p=[SELECT ID, Name FROM Profile WHERE Id=:userinfo.getProfileId() Limit 1];
for(opportunity opp : opps){
if(opp.Trigger_Help__c != null && p.Name <> 'System Administrator'){
settgs.add(opp.Trigger_Help__c);
}
}
for(opportunity op : trigger.new){
if(settgs.contains(op.Trigger_Help__c)){
op.adderror('An Opportunity of this type already exists on this Account. Please contact a system administrator with questions.');
}
}
}​
I am having trouble writing the test class and I am clueless as always. I have the following written, but I am lost as to what I actually need to be doing:
#isTest
private class TestleadDuplicatePreventer {
#isTest static void TestleadDuplicatePreventerwithOneOpp() {
Account acct = new Account(Name='Test Account');
insert acct;
Opportunity opp = new Opportunity(Name=acct.Name + ' Opportunity',
StageName='Open Opportunity',
CloseDate=System.today().addMonths(1),
Facility__c='Tacoma WA',
Oppty_Type__c='UCO Service',
AccountID=acct.Id);
insert opp;
Test.startTest();
opp= new Opportunity(Name='Opportunity Test',
StageName='Open Opportunity',
CloseDate=System.today().addMonths(2),
Facility__c='Tacoma WA',
Oppty_Type__c='UCO Service',
Account=[SELECT ID from Account WHERE Account.Name='Test Account']);
try
{
insert opp;
}
catch(Exception duplicate)
{
}
Test.stopTest();
}
}​
Any and all help is appreciated!!
Not sure exactly what your requirements were for your project, you can probably get this done without code doing a rollup sum field that counts the opportunities tied to an account that has the type used in Trigger_Help__c and then put a validation on opportunity that when ISNEW() if Account.Count_Of_Type__c > 0 causes the validation to fire OR on opportunity create a hidden field that is unique that is the concatenation of the account id and the Opportunity type which can be set by workflow or the process builder. Tis would prevent duplicate types from being added for a given account.
But to your original question: In your test I dont see you set the Trigger_Help__c field unless that is set by automation. Also when creating your second opp you dont need to do the query of account you can just use the acct.Id from earlier in the code.
For your catch you need to assert that the e.getMessage() is the message you expect from your add error in the trigger.
You probably also need to do a separate where you runAs(admin_user) where admin_user is a user that you create with the System administrator profile to ensure that you dont get an error when you run as the admin user, along the lines of:
#isTest
private class TestleadDuplicatePreventer {
#isTest static void TestleadDuplicatePreventerwithOneOpp() {
Account acct = new Account(Name='Test Account');
insert acct;
Opportunity opp = new Opportunity(Name=acct.Name + ' Opportunity',
StageName='Open Opportunity',
CloseDate=System.today().addMonths(1),
Facility__c='Tacoma WA',
Oppty_Type__c='UCO Service',
AccountID=acct.Id);
insert opp;
Test.startTest();
opp= new Opportunity(Name='Opportunity Test',
StageName='Open Opportunity',
CloseDate=System.today().addMonths(2),
Facility__c='Tacoma WA',
Oppty_Type__c='UCO Service',
AccountId=acct.Id);
try
{
insert opp;
}
catch(Exception duplicate)
{
System.assertEquals('An Opportunity of this type already exists on this Account. Please contact a system administrator with questions.', duplicate.getMessage())
}
//this should probably be in a separate test method
Profile p = [SELECT Id FROM Profile WHERE Name='System Administrator'];
User u2 = new User(Alias = 'newUser', Email='newuser#testorg.com',
EmailEncodingKey='UTF-8', LastName='Testing', LanguageLocaleKey='en_US',
LocaleSidKey='en_US', ProfileId = p.Id,
TimeZoneSidKey='America/Los_Angeles', UserName='newuser#testorg.com');
System.runAs(u2) {
insert opp;
}
Test.stopTest();
}
}​

Apex Trigger- how to update all case owners using trigger?

when user clicks on case to change caseOwner/user, i have to add this user to all associated cases(per customer). I am using below code ,but it is updating only one record/case. i can see in debug logs all cases were updated with latest owner but not really updated/stored in caseObject. please help me this.
trigger caseAssignment on Case (after insert, after update) {
set<id> ownerId = new Set<Id>();
set<id> customerId = new set<Id>();
for(Case caseobj : trigger.new){
ownerId.add(caseobj.OwnerId);
customerId.add(caseobj.AccountId);
}
for( User user:[Select id, FirstName, LastName from user where Id IN :ownerId]){
for(Case cas : [Select Id, OwnerId, First_Name__c, CaseNumber, AccountId From Case where AccountId IN: customerId]){
cas.OwnerId = user.Id;
}
}
Without an update DML statement, your code will have no effect on your data.
Probably should create an update list
i.e. list updateCaseList = new list()
Then in your inner for loop, add "cas" to updateCaseList and then at the end of the method, issue the update command (update updateCaseList).
Assuming that the logic you wrote is correct you're never updating the cases
trigger caseAssignment on Case (after insert, after update)
{
Set<Id> ownerId = new Set<Id>();
Set<Id> customerId = new Set<Id>();
List<Case> casesToUpdate = new List<Case>();
for(Case caseobj : trigger.new)
{
ownerId.add(caseobj.OwnerId);
customerId.add(caseobj.AccountId);
}
for( User user:[Select id, FirstName, LastName from user where Id IN :ownerId])
{
for(Case cas : [Select Id, OwnerId, First_Name__c, CaseNumber, AccountId From Case where AccountId IN: customerId])
{
Case c = new Case();
c.ID = cas.ID;
c.OwnerId = user.Id;
casesToUpdate.add(c);
}
}
update casesToUpdate;
}

using SOQL Query not able to fetch Account Applicant_ID__c

I have an external id in Account named Applicant_ID__c. I am using data loader to import data into salesforce Opportunity. Below is my mapping file content.
Date\ Cancelled=Date_Cancelled__c
Date\ Denied=Date_Denied__c
Date\ Decisioned=Date_Decisioned__c
App\ Status=Application_Status__c
Date\ Submitted=Application_Submitted__c
Closing\ Date=CloseDate
Application\ Source=Application_Source__c
Application\ Type=Application_Type__c
Application\ Sub-Type=Application_Sub_Type__c
App\ ID=App_ID__c
Property=Property_Name__r\:Property_Code__c
Applicant\ ID=Account\:Applicant_ID__c
Record\ Type\ ID=RecordTypeId
The above mapping is working correctly now what i want is to populate the opportunity name from trigger.
Below is my trigger content
trigger MapStatusToStageBeforeOpportunintyCreation on Opportunity (before insert, before update) {
for (Opportunity o : Trigger.New){
Account acc = [Select LastName From Account Where Applicant_ID__c =:Account:Applicant_ID__c];
o.Name =acc.LastName;
}
}
Thanks in advance.
That answer you created and excepted is going to blow up if insert 101 Opportunities, but if you want to use the Applicant_ID__c you can query it out in the account query
trigger MapStatusToStageBeforeOpportunintyCreation on Opportunity (before insert, before update)
{
Set<ID> acctIDS = new Set<ID>();
for (Opportunity o : Trigger.new)
{
if(o.AccountId != null)
{
acctIDS.add(o.AccountID);
}
}
Map<ID, Account> acctMap = new Map<ID, Account>([Select LastName, Applicant_ID__c From Account Where ID =: acctIDS]);
for(Opportunity o : Trigger.new)
{
for(ID acctID :acctMap.keySet())
{
if(o.AccountID == acctID)
{
o.Lastname = acctMap.get(acctID).LastName;
}
}
}
}
You are querying it wrong
First, you should Never Query in for loop
List'<'Opportuniy opplist = new list'<'Opportunity'>'();<Br/>
// Remove the single quotes <br/>
for (Opportunity o : Trigger.New){<Br/>
o.OpportunityApplicentID = o.Account.Applicant_ID__c;<Br/>
o.Name =acc.LastName;<Br/>
opplist.add(o);<Br/>
}
update opplist;
+Instead of using Applicant_ID__c =:Account:Applicant_ID__c this..Just use Applicant_ID__c =:Account.Applicant_ID__c.Don't use colon .Use dot operator

Resources