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
Mike RethageMike Rethage 

Create Equipment (custom) Object Records based on Contract Field Values

Hello,

Our business process demands automatic equipment record creation when a contract is activated.  When the contract is entered the user would enter numeric values for Product 1, Product 2 etc.  I want the flow to look-up those numeric values and create the number of new equipment records specified.  I.e Product1 =16 it would create 16 new equipment records under that account.  Additionally I would want information such as the Install Contact from the contracts Contact Role to be populated in all the equipment records.

Is an Apex trigger the best way to accomplish this?
Thanks!
Balaji BondarBalaji Bondar
Hi Mike,

You can consider below 2 approaches based on the business requirement for equipment creation.
1. You can write a trigger on Contract object On Update event:
trigger CreateEquipments on Contract (after update){
   if(Trigger.New && TriggerIsbefore){
	if(Contract contractObj : Trigger.New){
		if(contractObj.Status == 'Activated'){
		//Logic to check the Product count & equipment records creation
		}
	}
   }
}
2. You can write batch and schedule on daily basis to find out the daily activated contracts and create equipments accordingly.


Important :
If this is what you were looking for then please mark it as a "SOLUTION" or You can Click on the "Like" Button if this was beneficial for you.
Mike RethageMike Rethage
Thanks Balaji - can you explain the check and creation logic.  I understand that this would be based on a contract activation which looks to be the piece your provided. That part can even be accomplished outside of apex.  To do the check against the product count is an apex trigger the only way to accomplish this or is there a more simplistic version.  If apex is the only way can you please explain that part of the code, please?
pigginsbpigginsb
Hi, Mike.

It sounds like a trigger to me, because you will want to instantiate a number of records based on the numeric values entered on the Contract. I'm not sure if a flow could accomplish the same task.

This would be an After Update trigger on Contract, as it would fire immediately upon contract update, meeting your automatic record creation on contract activation.

The trigger should first check whether the status of each contract has changed to 'Activated' by comparing against the status values for each record in the trigger.oldMap variable. The Ids of the contracts whose status has changed to 'Activated' would be collected into a set in order to query for ContractContactRoles where the ContractId is in this set and the Role = 'Install Contact' (I'm assuming this is identified on the ContractContactRole record). You will then have a list of ContractContactRoles for your Install Contacts where the Contract has just changed to 'Activated'
Set<Id> activatedContractIds = new Set<Id>();

for (Contract each : trigger.new) {
    if (each.Status == 'Activated' && trigger.oldMap.get(each.Id).Status != 'Activated')
        activatedContractIds.add(each.Id);
}

List<ContractContactRole> contactRoles = [select ContractId, ContactId from ContractContactRole where Role = 'Install Contact' and ContractId in :activatedContractIds];
The ContractId from each query result can be used to get the Contract from trigger.newMap, which will have with it the product counts and the AccountId. You may need to be careful that a product count field actually contains a value before you try to use it.
List<Equipment__c> recordsToInsert = new List<Equipment__c>();

for (ContractContactRole eachRole : contactRoles) {
    Contract eachContract = trigger.newMap.get(eachRole.ContractId);

    // this section represents using a product count to add a number of records to list to be inserted
    if (eachContract.Product1__c != null) {
        for (Integer i = 0; i < eachContract.Product1__c; i++) {
            Equipment__c newEQPT = new Equipment__c (
                Account__c = eachContract.AccountId,
                Contact__c = eachRole.ContactId,
                Type__c = 'Product 1'
            );

            recordsToInsert.add(newEQPT);
        }
    }
}

insert recordsToInsert;
I am making plenty of assumptions here about how the equipment record is set up, and I haven't tested any of this code. Even still it should give you an idea of how the information you are looking for is available using the trigger map variables and some SOQL. There is a number of equipment records being created, each populated with the contract's account id and the contact id of the Install Contact role.
Mike RethageMike Rethage
trigger CreateEquipments on Contract (after update){
Set<Id> activatedContractIds = new Set<Id>();

for (Contract each : trigger.new) {
    if (each.Status == 'In Progress' && trigger.oldMap.get(each.Id).Status != 'In Progress')
        activatedContractIds.add(each.Id);
}

List<ContractContactRole> contactRoles = [select ContractId, ContactId from ContractContactRole where Role = 'Install Contact' and ContractId in :activatedContractIds];

List<Equipment__c> recordsToInsert = new List<Equipment__c>();

for (ContractContactRole eachRole : contactRoles) {
    Contract eachContract = trigger.newMap.get(eachRole.ContractId);

    // this section represents using a product count to add a number of records to list to be inserted
    if (eachContract.DS_Systems__c != null) {
        for (Integer i = 0; i < eachContract.DS_Systems__c; i++) {
            Equipment__c newEQPT = new Equipment__c (
                Account__c = eachContract.AccountId,
                RecordTypeId = [Select Id From RecordType WHERE Name = 'Digital Signs'].Id
            );

            recordsToInsert.add(newEQPT);
        }
    }
}

insert recordsToInsert;
}
Based on the feedback above - this is what we came up with over the last month.  Can't get anythin to fire when in our sandbox.  Any additional insight is welcome!  Thanks for the help so far!
 
pigginsbpigginsb
A better approach to getting your record type id would be to query for it when the trigger fires then assign this value to each new Equipment__c record, instead of including a query in the FOR loop. By this there's a better chance of avoiding the SOQL query limit.
trigger CreateEquipments on Contract (after update) {

    Id digitalSignsId = [Select Id From RecordType WHERE Name = 'Digital Signs'].Id;
...
You could also use a RETURN; statement if the activatedContractIds set is empty, to avoid performing unnecessary SOQL, before querying for any ContractContactRoles. This is not critical, but it's another way to avoid hitting the TOO MANY SOQL limit.

As for getting the expected behavior from the trigger
  • you'll want to make sure the trigger is active (sorry, I have seen it happen).
  • Also, make sure your contract status value is spelled correctly.
  • Check that the correct ContractContactRoles are in place, and that the role name is spelled correctly, as the ContractContactRole FOR loop will not even be entered if no ContractContactRole records are found, meaning there's no chance of creating the eqpt records.
  • If DS_Systems__c is zero, then no eqpt records will be created either.
That's all I can think of that would prevent it from generating the expected records. Hope it helps!