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
Developer.mikie.Apex.StudentDeveloper.mikie.Apex.Student 

validation Trigger Please help

Hi there,

I was helped wih this brilliant trigger by a scotty McClung, but correspondence recently died down. He had a brilliant idea, where I used my current business model to create a validation rule. It works, but it is not finished just yet.

Basically I have account, account has a child service__c. A user will add a service to an account, to do so they will select the picklist service_name__c and service_type__c, which will use a trigger that has an soql querry which finds the record from destiny_product_and_services__c and therefore will autofill formula fields on the service record.

I have added another field called macimum records to the destiny_products_and_service__c database. The trigger checks to see if the services attached to the account is greater than the number set in maximum records and if so will error.

This all works properly, except it only runs off service_name__c rather than both service_name__c and service_type__c. I was wondering if someone would give me a hand on either making it so that it will find the exact record through service_name__c and service_type__c or run by the actual record name field.

If it helps, in the database object - the name of the record is basically service_name__c (service_type__c).

E.g. A record with service_name__c = advantage program and service_type__c = new client would be:

Advantage Program (new client).

Thank you in advance for your time

To further explain, Destiny_rpoducts_and_services and service_c have two same fields which is two picklist service_name__c and service_type__c on service__c and the same but texts fields on Destiny_products_and_services. A trigger is used to take the picklist options and autofill the correct lookup.

This is my code:

Trigger ServiceValidation2 on Service__c (before insert) {

  
  //Populate a map of the ServiceRules object using the service name as the key
  Map<String, Destiny_Products_and_Services__c> mapServiceRules = new Map<String, Destiny_Products_and_Services__c>();
  for(Destiny_Products_and_Services__c objRule : [SELECT Name, MaximumRecords__c
                                                  FROM Destiny_Products_and_Services__c]) {
    mapServiceRules.put(objRule.Name, objRule);
  }
  
  //Populate a set of account_ids to use in your SOQL query
  Set<Id> setAccountIds = new Set<Id>();
  for(Service__c objService : trigger.new) {
    setAccountIds.add(objService.Account__c);
  }
  
  //Populate a map of the aggregate query results
  Map<String, Map<String, Decimal>> mapResults = new Map<String, Map<String, Decimal>>();
  AggregateResult[] groupedResult = [SELECT Account__c, service_name__c, Service_type__c, Count(Id) countOfRecords
                                     FROM Service__c
                                     WHERE Account__c IN :setAccountIds
                                     GROUP BY Account__c, Service_name__c, Service_type__c];

  for(AggregateResult ar : groupedResult) {
    if(!mapResults.containsKey((String)ar.get('Account__c'))) {
      mapResults.put((String)ar.get('Account__c'), new Map<String, Decimal>());
    }
    mapResults.get((String)ar.get('Account__c')).put((String)ar.get('Service_Name__c'), (Decimal)ar.get('countOfRecords'));
  }

  //Iterate through your list of Service__c records and generate an error for any 
  //that have a matching Account__c in the map
  for(Service__c objService : trigger.new) {
    if(mapServiceRules.containsKey(objService.Service_Name__c) && mapServiceRules.get(objService.Service_Name__c).MaximumRecords__c != null) {    //If there is no rule set ignore the record
      if(mapResults.containsKey(objService.Account__c)) {
        if(mapResults.get(objService.Account__c).containsKey(objService.Service_Name__c)) {

          if(mapResults.get(objService.Account__c).get(objService.Service_Name__c) >= mapServiceRules.get(objService.Service_Name__c).MaximumRecords__c) {
            objService.addError('This service has already been added the maximum number of times');
          }
      }
      }
    }
  }
}
Best Answer chosen by Developer.mikie.Apex.Student
Swati GSwati G
Hi Mikie,

Few questions here,
Is name field of Destiny_Products_and_Services__c is same as service type of service object?
As you are creating map mapServiceRules which contains key as name and value as count. And retriving values from map using service type of service object.

When we do group by 3 fields we get result like below

Account__c  Service Name   Service Type  Count
1                  A                     Type 1            21
1                  A                     Type 2            13
1                  B                     Type 1             2
1                  B                     Type 2             5
2                  A                     Type 1             6

You can store value in map of map of map i.e. you will require to use map 3 level deep. So your map should be Map<String,Map<String,Map<String,Decimal>>>

You can populate map as shown below:

Map<String, Map<String,Map<String, Decimal>>> mapResults = new Map<String, Map<String,Map<String, Decimal>>>();

for(AggregateResult ar : groupedResult) {
    String account = (String)ar.get('Account__c');
    String serviceName = (String)ar.get('Service_Name__c');
    String serviceType = (String)ar.get('Service_type__c');
    Decimal countofRecords = (Decimal)ar.get('countOfRecords');
    if(!mapResults.containsKey(account)) {
       Map<String,Map<String,Decimal>> serviceNameMap = New Map<String,Map<String,Decimal>>();
       serviceNameMap.put(serviceName, new Map<String,Decimal>{ serviceType => countofRecords});
       mapResults.put(account, serviceNameMap);
    }
   else{
       Map<String,Map<String,Decimal>> serviceNameMap = mapResults.get(account);
       if(!serviceNameMap.containsKey(serviceName)){
           Map<String,Decimal> serviceTypeMap = New Map<String,Decimal>{ serviceType => countofRecords };
           mapResults.get(account).put(serviceName,serviceTypeMap);
       }
      else{
           mapResults.get(account).get(serviceName).put(serviceType,countofRecords);
      }
   }
}

And when you want to check, you will need to get value from map as below:

if(mapResults.containsKey(objService.Account__c)) {
        if(mapResults.get(objService.Account__c).containsKey(objService.Service_Name__c)) {
           if(mapResults.get(objService.Account__c).get(objService.Service_Name__c).containsKey(objService.Service_Type__c)){
             if(mapResults.get(objService.Account__c).get(objService.Service_Name__c).get(objService.Service_Type__c) >=  
                                     mapServiceRules.get(objService.Service_Name__c).MaximumRecords__c) {
                objService.addError('This service has already been added the maximum number of times');
             }  
         }
      }
 }








All Answers

Swati GSwati G
Hi Mikie,

Few questions here,
Is name field of Destiny_Products_and_Services__c is same as service type of service object?
As you are creating map mapServiceRules which contains key as name and value as count. And retriving values from map using service type of service object.

When we do group by 3 fields we get result like below

Account__c  Service Name   Service Type  Count
1                  A                     Type 1            21
1                  A                     Type 2            13
1                  B                     Type 1             2
1                  B                     Type 2             5
2                  A                     Type 1             6

You can store value in map of map of map i.e. you will require to use map 3 level deep. So your map should be Map<String,Map<String,Map<String,Decimal>>>

You can populate map as shown below:

Map<String, Map<String,Map<String, Decimal>>> mapResults = new Map<String, Map<String,Map<String, Decimal>>>();

for(AggregateResult ar : groupedResult) {
    String account = (String)ar.get('Account__c');
    String serviceName = (String)ar.get('Service_Name__c');
    String serviceType = (String)ar.get('Service_type__c');
    Decimal countofRecords = (Decimal)ar.get('countOfRecords');
    if(!mapResults.containsKey(account)) {
       Map<String,Map<String,Decimal>> serviceNameMap = New Map<String,Map<String,Decimal>>();
       serviceNameMap.put(serviceName, new Map<String,Decimal>{ serviceType => countofRecords});
       mapResults.put(account, serviceNameMap);
    }
   else{
       Map<String,Map<String,Decimal>> serviceNameMap = mapResults.get(account);
       if(!serviceNameMap.containsKey(serviceName)){
           Map<String,Decimal> serviceTypeMap = New Map<String,Decimal>{ serviceType => countofRecords };
           mapResults.get(account).put(serviceName,serviceTypeMap);
       }
      else{
           mapResults.get(account).get(serviceName).put(serviceType,countofRecords);
      }
   }
}

And when you want to check, you will need to get value from map as below:

if(mapResults.containsKey(objService.Account__c)) {
        if(mapResults.get(objService.Account__c).containsKey(objService.Service_Name__c)) {
           if(mapResults.get(objService.Account__c).get(objService.Service_Name__c).containsKey(objService.Service_Type__c)){
             if(mapResults.get(objService.Account__c).get(objService.Service_Name__c).get(objService.Service_Type__c) >=  
                                     mapServiceRules.get(objService.Service_Name__c).MaximumRecords__c) {
                objService.addError('This service has already been added the maximum number of times');
             }  
         }
      }
 }








This was selected as the best answer
Developer.mikie.Apex.StudentDeveloper.mikie.Apex.Student
Thank you for your time