1. Create a field on the Quote Line item(Serial No (Text)) and populate increment values once a Quote Line Item is inserted. Let’s say if we add 3 products then the sequence would be 1,2,3, Now if we delete 2 and again add one more product that would be 4, It does not matter whether the product is getting deleted or not.
The very first thing we have to do is create a field with the name “Serial No” of datatype “Text” in a “Quote Line Item” (also called as QuoteLineItem). Later check whether there is “Product” on the related tab of “Quote”. So when you add a new “Product” in a particular Quote this Trigger is going to work. And also we will write a trigger on insert because the question tells about adding a product.
Want to Learn Salesforce Flows? Learn with our Salesforce Flow Course
Here we are using “before insert” because when we add a new Product on a particular Quote then we also need to update the Serial No field of that particular QuoteLineItem. And if we use ” after insert ” then the QuoteLineItem field will become read-only and we will not be able to update that QuoteLineItem.
In the below code, we are getting all the QuoteId in one Set so that we can get all the QuoteLineItem of that particular Quote and the Serial No of that Quote Line Item. We are also using Map<Id, String> where Id contains QuoteId and String contains the new Serial No of that particular QuoteLineItem. We are looping the QuoteLineItem’s which we got through the query and checking if the Serial No field is null, if it is null then we are making the Serial No field as 1. If not then we are getting the last number of the old value of the Serial No and converting it to Integer because we need to Increment it and append it. Then we are making changes in the Serial No field then adding the QuoteLineItem to List so that we can update it later, and also adding the values to the Map. After this, we are updating the QuoteLineItem’s which was previously present, and in which we made changes for the Serial No. Now the only thing left is changing the Serial No field of the newly inserting QuoteLineItem, so we loop the inserting List of Quote and check if its QuoteId is present in Map, if present then get that Map value and place it to Serial No field else we are just passing 1 to that Serial No field.
xxxxxxxxxx
trigger updateQuoteListItemSerialNo on QuoteLineItem (before insert) {
Set<Id> quoteId = new Set<Id>();
for(QuoteLineItem quoProd:Trigger.new){
quoteId.add(quoProd.QuoteId);
}
List<QuoteLineItem> quoProdListTOUpdate = new List<QuoteLineItem>();
//Map of Quote Id and Serial No of QuoteLineItem
Map<Id,String> quoIdSerialNoMap =new Map<Id,String>();
List<QuoteLineItem> quoteProList =[Select Id,Serial_No__c,QuoteId from QuoteLineItem where QuoteId=:quoteId];
if(quoteProList.size()>0){
for(QuoteLineItem quoProObj:quoteProList){
if(quoProObj.Serial_No__c!=null){
//Getting the last number of the Serial No field
String lastword = quoProObj.Serial_No__c.right(1);
//converting String into Integer so that I can increment the last number which is already there in the SerialNo field
Integer num = Integer.valueOf(lastword);
num++;
//appending the incremented number
quoProObj.Serial_No__c=quoProObj.Serial_No__c+','+(num);
quoProdListTOUpdate.add(quoProObj);
//adding it to Map so that I can make changes for the QuoteLineItem which is newly getting inserted
quoIdSerialNoMap.put(quoProObj.QuoteId,quoProObj.Serial_No__c);
}
else{
//if the Serial No Field is null then it means there is no QuoteLineItem present so by default we are passing 1
quoProObj.Serial_No__c='1';
quoProdListTOUpdate.add(quoProObj);
}
}
}
if(quoProdListTOUpdate.size()>0){
update quoProdListTOUpdate;
}
for(QuoteLineItem quoProd:Trigger.new){
/*Checking if the QuoteId of the newly inserted QuoteLineItem is present in the Map. If present we are passing that Serial No to the newly inserted QuoteLineItem else we are just adding 1 because this is the first OpportunityLineItem getting added*/
if(quoIdSerialNoMap.containsKey(quoProd.QuoteId)){
quoProd.Serial_No__c = quoIdSerialNoMap.get(quoProd.QuoteId);
}
else{
quoProd.Serial_No__c='1';
}
}
}
2. Do account private by default first, Once create an account that account should be shared with another user also,
Refer any user to share the newly created account.
In this trigger when an Account is created we need to share it with another user so the trigger will be on Account Object. As the question itself tells that “Once create an account” we need to use the “insert” operation. After the record is created then only we will be able to share so we need to use “after” here. Now let me get one Salesforce User (I have directly searched for one Salesforce User based on LastName which is not necessary to be LastName but you can also go according to Username or Email or anything else.). Storing the User Id in a String variable. Then I’m looping all the Account records which are getting inserted and created an Instance of AccountShare (This Object contains the Account record access of a particular record with a group or user. Each particular Object has its own Share Object.). Once I create the Instance I assign the required field where “ParentId” field tells which record you are giving access, “UserOrGroupId” field tells which group or user is having access, “AccountAccessLevel” field defines the type of access that has to be given for Account and “OpportunityAccessLevel” field defines the type of access that has to be given for Opportunity. Now I add the instance to a list for later insertion. You can see I have used “Database.SaveResult[] jobShareInsertResult = Database.insert(stdShares,false);” for inserting the record so that during insertion if some error occurs I would be able to track it. Now I loop all the Database.SaveResult and check if the insertion has not been successful, if not then I’m displaying the error that occurred.
xxxxxxxxxx
trigger sharingAccountWithAnotherUser on Account (after insert){
List<AccountShare> accShares = new List<AccountShare>();
//Getting a Random Single User
User u =[select Id,LastName from User where LastName=:'umesh'];
String userId= u.Id;
for(Account acc : Trigger.new){
//Creating an instance of AccountShare which will give access to a record to a particular group or a user
AccountShare accRecord = new AccountShare();
accRecord.AccountId= acc.Id;
accRecord.UserOrGroupId =u.Id;
accRecord.AccountAccessLevel = 'edit';
accRecord.OpportunityAccessLevel='read';
accRecord.RowCause ='Manual';
accShares.add(accRecord);
}
//Creating a Database.SaveResult instance so that if error occurs we can track it.
Database.SaveResult[] jobShareInsertResult = Database.insert(accShares,false);
Integer i=0;
for(Database.SaveResult sr : jobShareInsertResult){
//Checking if the record was not successfully inserted.
if(!sr.isSuccess()){
Database.Error err = sr.getErrors()[0];
if(!(err.getStatusCode() == StatusCode.FIELD_FILTER_VALIDATION_EXCEPTION
&& err.getMessage().contains('AccessLevel'))){
//Showing the thrown error
trigger.newMap.get(accShares[i].AccountId).
addError(
'Unable to grant sharing access due to following exception: '
+ err.getMessage());
}
}
i++;
}
}
3. Create a custom object Student, and do Private OWD, Once A student record creates, automatically should be shared with another user who belongs to the profile Salesforce User.
In this trigger when a Student is created we need to share it with another user so the trigger will be on Student Object. As the question itself tells that the “Student is created” we need to use the “insert” operation. After the record is created then only we will be able to share so we need to use “after” here. Now let me get one Salesforce User (I have directly searched for one Salesforce User based on LastName which is not necessary to be LastName but you can also go according to Username or Email or anything else.). Storing the User Id in a String variable. Then I’m looping all the Student records which are getting inserted and created an Instance of Student__Share (This Object contains the Student record access of a particular record with a group or user. Each particular Object has its own Share Object.). Once I create the Instance I assign the required field where “ParentId” field tells which record you are giving access, “UserOrGroupId” field tells which group or user is having access and “AccessLevel” field defines the type of access. Now I add the instance to a list for later insertion. You can see I have used “Database.SaveResult[] jobShareInsertResult = Database.insert(stdShares,false);” for inserting the record so that during insertion if some error occurs I would be able to track it. Now I loop all the Database.SaveResult and check if the insertion has not been successful, if not then I’m displaying the error that occurred.
xxxxxxxxxx
trigger sharingStudentRecordWithSalesforceUser on Student__c (after insert) {
List<Student__Share> stdShares = new List<Student__Share>();
//Getting a Random Single User
User u =[select Id,LastName from User where LastName=:'umesh'];
String userId= u.Id;
for(Student__c std : Trigger.new){
//Creating an instance of Student__Share which will give access to a record to a particular group or a user
Student__Share stdRecord = new Student__Share();
stdRecord.ParentId = std.Id;
stdRecord.UserOrGroupId =u.Id;
stdRecord.AccessLevel = 'edit';
stdRecord.RowCause ='Manual';
stdShares.add(stdRecord);
}
//Creating a Database.SaveResult instance so that if error occurs we can track it.
Database.SaveResult[] jobShareInsertResult = Database.insert(stdShares,false);
Integer i=0;
for(Database.SaveResult sr : jobShareInsertResult){
//Checking if the record was not successfully inserted.
if(!sr.isSuccess()){
Database.Error err = sr.getErrors()[0];
if(!(err.getStatusCode() == StatusCode.FIELD_FILTER_VALIDATION_EXCEPTION&& err.getMessage().contains('AccessLevel'))){
//Showing the thrown error
trigger.newMap.get(stdShares[i].ParentId).addError('Unable to grant sharing access due to following exception: '+ err.getMessage());
}
}
i++;
}
}
4. Create a field on User Object “Count”, On Account update Increment 1 in count field and on delete of account decrement count by 1.
For this trigger the scenario tells “On Account Update” so we need to write the trigger on Account and also use “update” another part of the scenario tells “On delete of account” which means we also need to use “delete” now let’s decide whether it is “after” or “before”. Once the updating or deletion is done then we need to make changes on User not even before updating so we will be using “after”.
Now let’s declare a Map<Id,Integer> to store which user is having how many updates at a time. Now let’s go for one scenario when an account record is updated, so whatever has to be done after the update let’s write it inside “if(Trigger.IsUpdate)” This condition will get true only when an updation is happening. Let’s loop all the updated records of the Account and check if the OwnerId of the Account contains in the Map<Id,Integer> if yes then we will increment that particular OwnerId’s Map value with 1 (because if the value exists in the Map that means there was already an Account record with the same OwnerId which was updated priorly) else we will add a new data to the Map with value 1. The same is done for the deletion inside “if(Trigger.IsDelete)” where the steps remain the same but instead of adding a number we decrement the number by 1. Later we check that the Map is not empty and get all the Users whose Id is present in the Map KeySet. Check if the User data is present and then Loop all of them and check if that User Id is in Map if yes we will check if the User Count is not null, if not null then we will add that Users map value to the Count field else we will assign the Users map value to the Count and also add that user to separate list. Check if that List of Users is not null then Update that List.
xxxxxxxxxx
trigger updateOwnerCount on Account (after update, before delete) {
//Map to add User Id and its count of updating and deletion
Map<Id,Integer> userIdRecordMap = new Map<Id,Integer>();
//Only runs during updation
if(Trigger.IsUpdate){
for(Account aObj:Trigger.new){
//Checking if the User Id exists in Map
if(userIdRecordMap.containsKey(aObj.OwnerId)){
Integer c = userIdRecordMap.get(aObj.OwnerId);
//incrementing Map value by 1
userIdRecordMap.put(aObj.OwnerId,c+1);
}
else{
//adding new data to map
userIdRecordMap.put(aObj.OwnerId,1);
}
}
}
//Only runs during deletion
if(Trigger.IsDelete){
for(Account aObj:Trigger.old){
//Checking if the User Id exists in Map
if(userIdRecordMap.containsKey(aObj.OwnerId)){
Integer c = userIdRecordMap.get(aObj.OwnerId);
//decrementing Map value by 1
userIdRecordMap.put(aObj.OwnerId,c-1);
}
else{
//adding new data to map
userIdRecordMap.put(aObj.OwnerId,-1);
}
}
}
List<User> userToUpdate = new List<User>();
if(userIdRecordMap.keySet().size()>0){
//getting all the users who were added to map
List<User> userWhereChangesMade = [Select Id,Count__c from User where Id=:userIdRecordMap.keySet()];
if(userWhereChangesMade.size()>0){
for(User u : userWhereChangesMade){
if(userIdRecordMap.containsKey(u.Id)){
Integer c = userIdRecordMap.get(u.Id);
if(u.Count__c!=null){
//adding the map value of a particular User to the Count field
u.Count__c = u.Count__c + c;
userToUpdate.add(u);
}
else{
//Assigning the map value of a particular User to Count field
u.Count__c = c;
userToUpdate.add(u);
}
}
}
}
}
if(userToUpdate.size()>0){
update userToUpdate;
}
}
5. When the account Status field is updated and if the related contact is more than zero then it shows an error and if the related contact is equal to zero then do nothing.
So as per the question we need to check if the related contact is equal to zero or not before updating the status field this is why we need to write the trigger on “before update”. Let’s now loop all the Accounts which is getting updated and check if the Status field is changing or not. If there is a change in Status then let’s add that Account Id to a Set<Id>. Now we have got all the Account IDs whose status is changing in a Set<Id>. Now let’s get all the accounts and their related contacts loop all the accounts and check if their related contacts are greater than 0. If yes then we will display the error ‘You can not Change the Status Field because the Account has related Contacts’.
xxxxxxxxxx
trigger errorOnChangeOfStatus on Account (before update) {
Set<Id> accId = new Set<Id>();
for(Account acc: Trigger.new){
//Checking if there is change in Status field.
if(acc.Status__c != Trigger.oldMap.get(acc.id).Status__c){
accId.add(acc.Id);
}
}
List<Account> accList = [Select Id,(Select Id from Contacts) from Account where Id=:accId];
if(accList.size()>0){
for(Account a : accList){
//Checking if there is an related Contact
if(a.Contacts.size()>0){
//Error message to shown
trigger.new[0].addError('You can not Change the Status Field because the Account has related Contacts');
}
}
}
}
6. When the Opportunity Stage field is updated as Closed then it should check that its Product Serial No field is not empty, if it is empty show an error message.
The first thing we should do is we need to create a “Serial No” field on Product Object with datatype text. Now let’s see when we should call this trigger, here we trying to update the record, and on condition not getting satisfied we are showing an error. So here we are not updating, first, we are checking the condition then it is getting updated so this means that we have to use the “before update” trigger.
Now let’s get back to the part of the trigger, first, we need to loop all the Opportunity records which are updating and check if previously the opportunity wasn’t closed and also check if the opportunity is getting closed and add that particular Opportunity Id to a Set<Id>, now this Set<Id> has all the Ids of the Opportunities which are getting closed. Now let’s get all the OpportunityLineItems which is related to the Opportunity Ids which we have added in the Set<Id> before looping we need to check that the list is not empty then loop all the OpportunityLineItems and check if the Serial No field is null, if yes then we are displaying the error message.
xxxxxxxxxx
trigger errorWhileDClosingOppIfProductSerialNoIsEmpty on Opportunity (before update) {
Set<Id> oppId = new Set<Id>();
for(Opportunity opp:Trigger.new){
//Checking if the Opportunity was previously not closed and now that it is getting closed
if(Trigger.oldMap.get(opp.id).StageName!='Closed Won'&&Trigger.oldMap.get(opp.id).StageName!='Closed Lost'){
if(opp.StageName=='Closed Won' ||opp.StageName=='Closed Lost'){
oppId.add(opp.Id);
}
}
}
//Getting Products related to the opportunity updating
List<OpportunityLineItem> oppProList = [Select Id,OpportunityId,Serial_No__c from OpportunityLineItem where OpportunityId=:oppId];
if(oppProList.size()>0){
for(OpportunityLineItem oppProd:oppProList ){
//Checking if the serial no field is empty
if(oppProd.Serial_No__c==null){
//Error message to be shown
trigger.new[0].addError('To Close the Opportunity Its Product Serial Number Cannot be Empty');
}
}
}
}
One thought on “36 Trigger Scenarios in Salesforce with Solution – Part 5”