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
Sunil Kandel 10Sunil Kandel 10 

Write a Trigger to check the records matches with all records

Hi,

I have custom object called ITEM with three Fields:
  1. CUSTOMER   (lookup field from Account)
  2. LOCATION (lookup from field Well__c)
  3. PRIME CUSTOMER (checkbox)
 
I have another custom field: DIRECT INVOICE where there are only two fields. This is kind of list of combination.
  1. CUSTOMER INVOICE  (lookup field from Account)
  2. LOCATION INVOICE (Lookup field from Well__c)
 
I need to write a trigger (after update, after insert) where if CUSTOMER and LOCATION from object ITEM matches with any of the records  with CUSTOMER INVOICE and LOCATION INVOICE from DIRECT INVOICE, then update a field PRIME CUSTOMER in item to TRUE.
 
I am maintaining a list of combination of customer invoice and location invoice in Direct invoice object, hence any new record of update record in object is to be checked with all records in Direct invoice to find if any records matches.
 
Thank you for your suggestion.
 
Best Answer chosen by Sunil Kandel 10
Adilson Arcoverde JrAdilson Arcoverde Jr

Hi Sunil,

First of all, you should use before insert and before update events, since you want to update trigger records. Trigger events of kind After, checks records as read-only.

Take a look at the following code:

trigger TriggerOportunidade on ITEM__c (before insert, before update ) {

    if( Trigger.isBefore && (Trigger.isInsert || Trigger.isUpdate) ) {
        List<ITEM__c> items = Trigger.new;

        Set<String> accountsIds = new Set<String>();
        Set<String> wellsIds = new Set<String>();
        for( ITEM__c item : items ) {
            if( item.Customer__c != null ) {
                accountsIds.add( item.Customer__c );
            }

            if( item.Location__c != null ) {
                wellsIds.add( item.Location__c );
            }
        }

        Map<String,Direct_Invoice__c> directInvoicesMap = new Map<String,Direct_Invoice__c>();
        for( Direct_Invoice__c di : [Select Customer_Invoice__c, Location_Invoice__c from Direct_Invoice__c where Customer_Invoice__c in :accountsIds and Location_Invoice__c in :wellsIds] ) {
            if( !directInvoicesMap.containsKey( di.Customer_Invoice__c + '-' +di.Location_Invoice__c ) ) {
                directInvoicesMap.put( di.Customer_Invoice__c + '-' +di.Location_Invoice__c, di );
            }
        }

        for( ITEM__c item : items ) {
            if( directInvoicesMap.containsKey( item.Customer__c + '-' + item.Location_Invoice__c ) ) {
                item.Prime_Customer__c = true;
            }
        }
    }
}

Hope that helps you somehow.
 

Best regards.

All Answers

Adilson Arcoverde JrAdilson Arcoverde Jr

Hi Sunil,

First of all, you should use before insert and before update events, since you want to update trigger records. Trigger events of kind After, checks records as read-only.

Take a look at the following code:

trigger TriggerOportunidade on ITEM__c (before insert, before update ) {

    if( Trigger.isBefore && (Trigger.isInsert || Trigger.isUpdate) ) {
        List<ITEM__c> items = Trigger.new;

        Set<String> accountsIds = new Set<String>();
        Set<String> wellsIds = new Set<String>();
        for( ITEM__c item : items ) {
            if( item.Customer__c != null ) {
                accountsIds.add( item.Customer__c );
            }

            if( item.Location__c != null ) {
                wellsIds.add( item.Location__c );
            }
        }

        Map<String,Direct_Invoice__c> directInvoicesMap = new Map<String,Direct_Invoice__c>();
        for( Direct_Invoice__c di : [Select Customer_Invoice__c, Location_Invoice__c from Direct_Invoice__c where Customer_Invoice__c in :accountsIds and Location_Invoice__c in :wellsIds] ) {
            if( !directInvoicesMap.containsKey( di.Customer_Invoice__c + '-' +di.Location_Invoice__c ) ) {
                directInvoicesMap.put( di.Customer_Invoice__c + '-' +di.Location_Invoice__c, di );
            }
        }

        for( ITEM__c item : items ) {
            if( directInvoicesMap.containsKey( item.Customer__c + '-' + item.Location_Invoice__c ) ) {
                item.Prime_Customer__c = true;
            }
        }
    }
}

Hope that helps you somehow.
 

Best regards.

This was selected as the best answer
Rishab TyagiRishab Tyagi
Hello Sunil,

I would definitely go with the solution provided by Adilson, with the only change being I would use Set<String> instead of a Map<String,Direct_Invoice__c> as Set are lighter to use in code as compared to map which is heavier.

Also, I would like to suggest you that the approach you are using is bound to fail once DIRECT INVOICE records reach the total count of 50000. As we cannot query more than 50000 in a single execution. You might need to update the DIRECT INVOICE object and add a field of formula type to store the mapping value of 'item.Customer__c + '-' + item.Location_Invoice__c'. After that first, create a set of these values in the trigger code and then query only the records which are related to your need as an aggregate query having [formula field] wise count and then run the rest of the code from the trigger as it is.
Sunil Kandel 10Sunil Kandel 10
Hi Adilson/Rishab, Thank your for quick response. Appreciated. I am new to coding. I think it also needs apex class right? Will that be too much to ask for? Please suggest.
 
Adilson Arcoverde JrAdilson Arcoverde Jr
Hi Sunil.

The best practice is to decouple business logic to another apex class, usually named TriggerHandler.

There are a lot of patterns. You should take one the fulfills your needs.

In general terms, triggers control only the order of execution. For instance, you cloud rewrite the trigger this way:

trigger ItemTrigger(before insert, before update) {

   if(Trigger.isUpdate && Trigger.isBefore) {
.       TriggerHandler.doSomething();
  } else if (...) {
.}

And so on.

Regards.
 
Sunil Kandel 10Sunil Kandel 10
Hi Adilson, thank you very much for your quick response and support, I will do the same.

For Trigger, I have updated with the real API name for objects and fields. However, the field update trigger is not updating the field.
I realized that my customer field in the Item object is not a lookup but a formula field. Does that matter. I have checked all the fields and api name but still it is not updating the field.

Thank you


 
Rishab TyagiRishab Tyagi
Hello Sunil,

In this case please check if there are any process builder or workflows governing the field update. Flows usually run after the triggers and hence can update the changes made by triggers. Also, you can add system.debug lines at various points in the code to debug values of the records and check if everything is working correctly. Having a formula field should not affect the flow of execution in my opinion. There must be some other reason for the same.
Sunil Kandel 10Sunil Kandel 10
Thank you Adilson and Risab for your input and suggestion.
Appreciated.