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 Rule to Prevent more than one Child record with certain Picklist Option

Hey there,

I have Accounts and Accounts has a custom child object named Services__c. Services__c, are added to the account. I was wondering if it is possible to write a validation rule which would prevent the same service (which is picked through a picklist, which auto-updates a hidden lookup field) from getting added twice to an account. 

If more explanation is neededd, please do not hesitate to ask.

Thankyou
Best Answer chosen by Developer.mikie.Apex.Student
Scott McClungScott McClung
In that case, maybe we should be using the ID of the Destiny_Products_and_Services record instead of the Name and count the occurances of the id in the lookup field.

trigger ServiceValidation2 on Service__c (before insert) {

  Map<Id, Destiny_Products_and_Services__c> mapServiceRules = new Map<Id, Destiny_Products_and_Services__c>();
  
  mapServiceRules.putAll(SELECT Id, Name, MaximumRecords__c
                         FROM Destiny_Products_and_Services__c]); //we're using the ID as the key now
  
  //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, Destiny_Service__c, Count(Id) countOfRecords
                                     FROM Service__c
                                     WHERE Account__c IN :setAccountIds
                                     GROUP BY Account__c, Destiny_Service__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('Destiny_Service__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.Destiny_Service__c) && mapServiceRules.get(objService.Destiny_Service__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.Destiny_Service__c)) {
          if(mapResults.get(objService.Account__c).get(objService.Destiny_Service__c) >= mapServiceRules.get(objService.Destiny_Service__c).MaximumRecords__c) {
            objService.addError('This service has already been added the maximum number of times');
          }
        }
      }
    }
  }
}


All Answers

Ramu_SFDCRamu_SFDC
Hi Mikie, can you please elaborate the issue by giving some more information about the hidden field on services__c and how is it updated (is it via trigger or is it through a workflow) ? Also give an example of the complete workflow on how you create service__c. Thanks.


Developer.mikie.Apex.StudentDeveloper.mikie.Apex.Student
certainly, Thank you for your reply.

When I say hidden, I mean that it does not appear on page layouts, it is used as a way of creating a relationship between service__c and destiny_products_and_services__c which is sort of like a collection of records.

The user will select the service name and service type, this will auto-fill the lookup field as such:

Trigger Service_GrabReference on Service__c (before insert, before update) {
    
    List<Destiny_Products_and_Services__c> dpas = new List<Destiny_Products_and_Services__c>();
    for (Service__c  dp: trigger.new) {
       if(dp.Service_Name__c != null) {

           dpas = [Select Id from Destiny_Products_and_Services__c WHERE Destiny_Products_and_Services__c.Service_Name__c = :dp.Service_Name__c and Destiny_Products_and_Services__c.Service_Type__c =:dp.Service_Type__c ORDER BY Id LIMIT 1];
              if (dpas.size() > 0){
                 dp.Destiny_Service__c = dpas[0].Id;
              }         
       }
    }
}

As some services can be added more than once,w here as others have a limit of 1. The goal is to make it so that if an account already has a servie named 'X' attached, then another service 'X' cannot be added.

I hope I explained it well.

Thank you in advance for any help you can give me!

Ramu_SFDCRamu_SFDC
Hi Mikie, Thanks for the explanation, please follow the below approach to accomplish your requirement

1. Create a before trigger on service__c object
2. Create a set variable to get the list of all account id's entered in these service records.
3. Create another set variable and fetch all the Account id's that correspond to the service records that match the following crtieriasomething like "select accountid from service__c where accountid=<set variable> and picklistfield=<the option that you are checking>"
4. now loop through trigger.new records and check if the account id mentioned in the service record exist in the above set variable(declared in point 3). This means that the account record already has one service record with that picklist value selected. Throw the error accordingly.
Else, go ahead and create the record.

Hope this helps.
Developer.mikie.Apex.StudentDeveloper.mikie.Apex.Student
Thank you for the reply Ramu,

Would Something like this, but refering service rather than account status work:
trigger ServiceValidation on Service__c (before insert) {
// Iterate through the list of new records
    for (Service__c Ser : trigger.new) {
    if(ser.service_Name__c == 'Essential Property Education Only'){
        
        
        // We only care if there is one record
        List<Account_Status__c> resultList = [SELECT id, Name FROM Account_Status__c 
                        WHERE  Account__c = :ser.Account__c
                        AND    Destiny_Presentation_Service_Decision_N0__c = :Null];
         
         // Raise the error back if any records found        
         if (!resultList.isEmpty()) {
             System.debug('Found another copy ' + resultList[0].id + '  name is ' + resultList[0].Name);
             Ser.addError('This service has already been added');
         }
         }
    }

}
Is there anyway to do this using a validation rules? Perhaps with $object.<Service__c.ServiceName>. I ask because Service__c already has three triggers and I have heard that you can have too many triggers on an object. especially cosnidering that I would have to make one for each service.
Ramu_SFDCRamu_SFDC
Hi Mikie, Validation rules may not work here as they cannot handle complex formula's. Through validation rule formula we cannot refer to the other child records (service__c record) of the same parent account record, we cannot use validation rules.

Regarding your trigger, it would definately work. If there are any errors let me know.

You might have to tweak it further as it is not adviced to use soql query within for loop for it might hit governor limits during bulk transactions. To fix this, please move the soql query out of for loop by using map variables and you can then loop through the results
Developer.mikie.Apex.StudentDeveloper.mikie.Apex.Student
Thank you for the reply Ramu! Would you possibly be able to provide some example code on how I may do this. I am slowly understanding maps, just not 100% there yet.
Scott McClungScott McClung
Hi Mikie
Here's a way to rewrite your validation logic so it is properly 'bulkified'.  

trigger ServiceValidation on Service__c (before insert) {
  
  //Populate a set of account_ids to use in your SOQL query
  Set<Id> setAccountIds = new Set<Id>();
  for(Service__c objService : trigger.new) {
    if(objService.Service_Name__c == 'Essential Property Education Only') {
      setAccountIds.add(objService.Account__c);  
    }
  }
  
  //Populate a map of the query results
  Map<Id, Service__c > mapServices = new Map<Id, Service__c>();
  for(Service__c objService : [SELECT Id, Name 
                               FROM Service__c
                               WHERE Account__c IN :setAccountIds
                               AND Destiny_Presentation_Service_Decision_N0__c = null]) {
    mapServices.put(objService.Account__c, objService);
  }

  //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(mapServices.containsKey(objService.Account__c)) {
      Service__c objExistingService = mapServices.get(objService.Account__c);
      System.debug('Found another copy ' + objExistingService.Id + '  name is ' + objExistingService.Name);
      objService.addError('This service has already been added');
    }
  }
}

Based on your earlier statement, I replace the Account_Status__c object with the Service__c object.

Like you mentioned, it's not good design to write a trigger for each one of these Services if there are varying business rules on which ones restricted to only a single entry.  Without knowing more about your data model, here is one idea of how to solve it.
Create a custom object (for the sake the conversation we'll call it ServiceRules) and add one custom number field called MaximumRecords.
Add a record in this new object for each service that is included in the picklist on the Account object and if there is a maximum number of records enter it in MaximumRecords.  If there is no limit, just leave MaximumRecords empty.
Here's how you could write the validation rule leveraging that new object.

trigger ServiceValidation on Service__c (before insert) {
  
  //Populate a map of the ServiceRules object using the service name as the key
  Map<String, ServiceRules__c> mapServiceRules = new Map<String, ServiceRules__c>();
  for(ServiceRules__c objRule : [SELECT Name, MaximumRecords__c
                                 FROM ServiceRules__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, Name, Count(Id) countOfRecords
                                     FROM Service__c
                                     WHERE Account__c IN :setAccountIds
                                     AND Destiny_Presentation_Service_Decision_N0__c = null
                                     GROUP BY Account__c, Name];

  for(AggregateResult ar : groupedResult) {
    if(!mapResults.containsKey(ar.get('Account__c'))) {
      mapResults.put(ar.get('Account__c'), new Map<String, Decimal>());
    }
    mapResults.get(ar.get('Account__c')).put(ar.get('Name'), 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.Name) && mapServiceRules.get(objService.Name).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.Name) {
          if(mapResults.get(objService.Account__c).get(objService.Name) >= mapServiceRules.containsKey(objService.Name).MaximumRecords__c) {
            objService.addError('This service has already been added the maximum number of times');
          }
        }
      }
    }
  }
}

This allows you to set a different maximum for each Service and validate all of them with this single validation rule.
I didn't build out the data model to compile or test this example so there might be a couple corrections needed.  Just let me know.

Developer.mikie.Apex.StudentDeveloper.mikie.Apex.Student
Thank you so much for your reply Scott. This is an impressive bit of code. I will work on implementing it. 

i jsut had two quick questions: Would it be difficult to add other rules, other than maximum records later on?

Does every service record need a service rules attached? or is it just like a database of records (only one record for each service) and  multiple services from multiple accounts would apply to the one serviceRules record? 

If the answer is the latter, as I have a database of the records already, which the service object lookups to autofill fields. Could I add the service rules to each record there. Then when I add the service, it not only accesses the information of the service, but the rules aswell.
Thank you for your time

Developer.mikie.Apex.StudentDeveloper.mikie.Apex.Student
Hey scott,

Thank you so much for your help

I have tried to implement the code, I have changed it a bit as I do not need the Account_Status__c date field check if I already have the maximumcount field.

I keep default information on all my services in an object called destiny_Products_and_services__c. Basically, when a user adds a service to the account, they click new service - and pickthe service from a picklist (using a trigger the picklist autofills a lookup to destiny_Products_and_services__c which in turn autofills formula fields. I have added the maximumCount field to the destiny_Products_and_services__c object for every record and upon saving I got this error:

Error: Compile Error: unexpected token: '{' at line 35 column 55


This is the code after slightly changing it:

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, Name, Count(Id) countOfRecords
                                     FROM Service__c
                                     WHERE Account__c IN :setAccountIds
                                     GROUP BY Account__c, Name];

  for(AggregateResult ar : groupedResult) {
    if(!mapResults.containsKey(ar.get('Account__c'))) {
      mapResults.put(ar.get('Account__c'), new Map<String, Decimal>());
    }
    mapResults.get(ar.get('Account__c')).put(ar.get('Name'), 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.Name) && mapServiceRules.get(objService.Name).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.Name) {
          if(mapResults.get(objService.Account__c).get(objService.Name) >= mapServiceRules.containsKey(objService.Name).MaximumRecords__c) {
            objService.addError('This service has already been added the maximum number of times');
          }
        }
      }
    }
  }
}


Developer.mikie.Apex.StudentDeveloper.mikie.Apex.Student
I managed to solve that problem by adding some brackets. But then I got this eror, which I think is a little ahrder to solve:

Error Error: Compile Error: Incompatible key type Object for MAP<String,MAP<String,Decimal>> at line 26 column 7
Scott McClungScott McClung
Sorry Mikie, that's my fault.  I always forget that aggregateresults return objects and you have to cast their values in order to assign them.  Try this update.  Notice the '(String)' in front of ar.get('Account__c').  That casts the value being returned to a String so it matches the variable type we set for the map key.

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, Name, Count(Id) countOfRecords
                                     FROM Service__c
                                     WHERE Account__c IN :setAccountIds
                                     GROUP BY Account__c, Name];

  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('Name'), (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.Name) && mapServiceRules.get(objService.Name).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.Name) {
          if(mapResults.get(objService.Account__c).get(objService.Name) >= mapServiceRules.containsKey(objService.Name).MaximumRecords__c) {
            objService.addError('This service has already been added the maximum number of times');
          }
        }
      }
    }
  }
}


Scott McClungScott McClung
Looks like you figured out the question about this being a database of services.  Adding the rule to Destiny_Products_and_Services__c was a good idea.  Now any time you add a new service to that object, you can have a corresponding rule for it as well.

To answer your other questions, no it would not be difficult to add other requirements to this validation.  The actual test of the business rule in this case is this section
if(mapResults.get(objService.Account__c).get(objService.Name) >= mapServiceRules.containsKey(objService.Name).MaximumRecords__c) {
            objService.addError('This service has already been added the maximum number of times');
          }

You could simply insert additional tests immediately following this.  Just be sure to include the additional business rule fields from Destiny_Products_and_Services__c in the initial query.
Developer.mikie.Apex.StudentDeveloper.mikie.Apex.Student
Thank you for your reply Scott, the solution seemed to solve the issues with the map however I am getting another error that I am unable to solve. I have had a similar error with different code before, but normally I just need to add another = sign somehwere.

This is the error:

rror: Compile Error: Initial term of field expression must be a concrete SObject: Boolean at line 37 column 121.

Thank you so much for your reply and continued correspondence. 

Obviously it is one step at a time and I am focused on deploying this current, brlliant trigger. But, on the topic of further validation. Hypothetically, would it be a possibility of say adding a text field which lists required services to be completed before adding a certain service or is that not possible?


Developer.mikie.Apex.StudentDeveloper.mikie.Apex.Student
Hi Again Scotty,

Sorry to write back again, but the way I see it is. The less of your time I waste the better. So I am constantly just trying things to see if it will solve things. I hope it does not detriment your code, but I changed the call on line 37 column 121 from

mapServiceRules.containsKey(objService.Name).MaximumRecords__c

to

mapServiceRules.get(objService.Name).MaximumRecords__c

It allowed me to save, however upon testing the rule I received this error:

Error: Invalid Data.
Review all error messages below to correct your data.
Apex trigger ServiceValidation2 caused an unexpected exception, contact your administrator: ServiceValidation2: execution of BeforeInsert caused by: System.UnexpectedException: field 'Name' can not be grouped in a query call: Trigger.ServiceValidation2: line 19, column 1


Therefore, as destiny_products_and_services__c has the fields service_name__c and service_type__c in order to make the records unique and to supply the ability to SOQL request them in a trigger which sets the lookup via the two picklist choices I attempted swapping name for service_name__c, but received this error:

Error: Invalid Data.
Review all error messages below to correct your data.
Apex trigger ServiceValidation2 caused an unexpected exception, contact your administrator: ServiceValidation2: execution of BeforeInsert caused by: System.SObjectException: Invalid field Name for AggregateResult: Trigger.ServiceValidation2: line 28, column 1


I changed all the mentions of name to service_name__c and it started working. 

It works to the point where it prevents me from adding two services with the same service_name__c.  Is there any way to make it run off the exact service. Whether it be through name or service_name__c and service_type__c?

Thank you sooo much for your time. I appreciate it to no end.

Excuse the constantly changing problems and errors, I left this open as I was trying random things and it eventually worked. Like I said, I would have thought that it wold be best to waste as little time as possible of yours.

Thank you,

Mikie




Developer.mikie.Apex.StudentDeveloper.mikie.Apex.Student
Woops, sorry this is the current 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, Count(Id) countOfRecords
                                     FROM Service__c
                                     WHERE Account__c IN :setAccountIds
                                     GROUP BY Account__c, Service_name__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');
          }
        }
      }
    }
  }
}


Scott McClungScott McClung
Good job troubleshooting the code.
When you say the 'exact service' are you saying that it's the combination of service_name__c and service_type__c that identifies a unique record?
Developer.mikie.Apex.StudentDeveloper.mikie.Apex.Student
Yes exactly right. When a user adds a service to an accout, they pick two picklists and the lookup field is filled via:

Trigger Service_GrabReference on Service__c (before insert, before update) {
    
    List<Destiny_Products_and_Services__c> dpas = new List<Destiny_Products_and_Services__c>();
    for (Service__c  dp: trigger.new) {
       if(dp.Service_Name__c != null) {

           dpas = [Select Id from Destiny_Products_and_Services__c WHERE Destiny_Products_and_Services__c.Service_Name__c = :dp.Service_Name__c and Destiny_Products_and_Services__c.Service_Type__c =:dp.Service_Type__c ORDER BY Id LIMIT 1];
              if (dpas.size() > 0){
                 dp.Destiny_Service__c = dpas[0].Id;
              }         
       }
    }
}



 
and as such all formula fields are also filled. So there may be 4 records with service_name__c = Advantage program (in destiny_products_and_services__c), each one of these records will have a different service_type__c (e.g. New client, Upgrade EPE, etc).....then the record name will appear as Advantage program (new client).

So, from account a user will select service_name__c ='Advantage Program' and service_type__c =' New client' and the trigger will select from object 'destiny_products_and_services__c' ----- Advantage Program (Newclient) and there fore all formula fields such as price and inclusions, etc will be added to relevent formula fields.

Thank you for your time.
Scott McClungScott McClung
In that case, maybe we should be using the ID of the Destiny_Products_and_Services record instead of the Name and count the occurances of the id in the lookup field.

trigger ServiceValidation2 on Service__c (before insert) {

  Map<Id, Destiny_Products_and_Services__c> mapServiceRules = new Map<Id, Destiny_Products_and_Services__c>();
  
  mapServiceRules.putAll(SELECT Id, Name, MaximumRecords__c
                         FROM Destiny_Products_and_Services__c]); //we're using the ID as the key now
  
  //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, Destiny_Service__c, Count(Id) countOfRecords
                                     FROM Service__c
                                     WHERE Account__c IN :setAccountIds
                                     GROUP BY Account__c, Destiny_Service__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('Destiny_Service__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.Destiny_Service__c) && mapServiceRules.get(objService.Destiny_Service__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.Destiny_Service__c)) {
          if(mapResults.get(objService.Account__c).get(objService.Destiny_Service__c) >= mapServiceRules.get(objService.Destiny_Service__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
Hmm, now it no longer works. That really is weird. What coud be the problem do you think?
Developer.mikie.Apex.StudentDeveloper.mikie.Apex.Student
Its weird I went back to try again and it suddenly worked. Thank you so much!!!