-2

Use case : Once contract is created with :

  1. record type = B2C
  2. AccountId = Case.AccountId
  3. ContactId = Case.ContactId
  4. GEG_Correspondance_Offre_Produit__r.Type_Energie__c = Case.GEG_Correspondance_Offre_Produit__r.Type_Energie__c = Electricité ( 0 in SFDC )

Then relate cases (RecodType Sousscription ) with Contract just created.

Trigger

trigger Contrat_Trigger on Contract (after insert) {


List<Case> cases = [Select id , GEG_Contract_Requete__c , ContactId, AccountId from case where Status ='Clôturée' and RecordType.Name  = 'Demande de Souscription' and GEG_Correspondance_Offre_Produit__r.Type_Energie__c = '0'];

List<Id> ids =  new List<Id>();
for (Contract con : Trigger.new){
    ids.add(con.Id);
    
}

for (Contract con : [select Id , RecordType.Name from contract where Id in : ids]){
    system.debug('contraaact' +con); 
    if (con.RecordType.Name == 'GEG_B2C'){
    for(Case c : cases){
        
            if(con.AccountId == c.AccountId && con.GEG_Contact_Titulaire__c == c.ContactId && con.GEG_Correspondance_Offre_Produit__r.Type_Energie__c == c.GEG_Correspondance_Offre_Produit__r.Type_Energie__c){
                c.GEG_Contract_Requete__c = con.Id;
            }
           cases.add(c); 
        system.debug('Caseeee'+c);
        }
        
        
    }
    update cases;
    system.debug('Caseeee'+cases);
    
    
    
}

}

Can anyone tell me why my trigger doesn't work ?

5
  • 2
    Does this answer your question? How do I bulkify queries for directly or indirectly related records? - specifically look at "I need data from indirectly related records based on one or more input record fields" – Phil W Nov 2 '20 at 16:00
  • 1
    Please be specific about how exactly this trigger "doesn't work". It is certainly very inefficient, because it does not use the right bulkification pattern given in the question Phil linked. It's likely also behaving incorrectly because your || logic appears not to match your expectation, which seems like it's an &&. – David Reed Nov 2 '20 at 16:01
  • David - Phil's link doesn't help me at all plus my logic i've putted in description is the same as the logic putted in trigger . if not , thats why i'm asking for help – Farkous Nov 2 '20 at 16:03
  • No, it isn't. You specify four criteria that must be met, but because you have written your logic using the OR operator (||) you are only checking that any one of those criteria is true. Additionally, your check on Energie__c does not match the statement in your description. – David Reed Nov 2 '20 at 16:06
  • Again, you can help us help you by being specific about what isn't working. Specify in your question the input data you provide, the behavior that you expect, and the incorrect output that you receive. Otherwise we are just guessing about what the problem is. – David Reed Nov 2 '20 at 16:07
1

A short critique:

  1. Don't re-query the Contracts; you have all the data you need in the trigger context. Instead, simply use the Schema API, specifically DescribeSObjectResult's getRecordTypeInfosByDeveloperName, to help you get the Record Types with their names and then get the ID for the record type you care about.
  2. Make sure to explicitly load the Type_Energie__c values for the various related "GEG_Correspondance_Offre_Produit__r" data, as it stands this is never queried. You can do this using I just need data from directly related records but I'm in a trigger.
  3. Never use nested loops like this. Use the approach covered in I need data from indirectly related records based on one or more input record fields. This will help you easily find the cases that match the contracts, you just have to adapt it to use the AccountID, GEG_Contact_Titulaire__c/ContactID and the specifically loaded GEG_Correspondance_Offre_Produit__r.Type_Energie__c data.

Taking this into consideration (and making some assumptions about your object model which isn't detailed in your question), I believe you want something like:

trigger Contract_Trigger on Contract (after insert) {
    // Get the ID of the required record type
    Id b2c = Schema.SObjectType.Contract.getRecordTypeInfosByDeveloperName().get('GEG_B2C').recordTypeId;

    // Collect the required produit, account and contact IDs from the contracts being inserted
    Set<Id> produitIds = new Set<Id>();
    Set<Id> accountIds = new Set<Id>();
    Set<Id> contactIds = new Set<Id>();

    for (Contract contract : Trigger.new) {
        produitIds.add(contract.GEG_Correspondance_Offre_Produit__c);
        accountIds.add(contract.AccountId);
        contactIds.add(contract.GEG_Contact_Titulaire__c);
    }

    // Work out which of the contract's produit's have zero energie
    Set<Id> produitWithZeroEnergie = new Set<Id>();

    for (GEG_Correspondance_Offre_Produit__c produit : [
            SELECT Id, Type_Energie__c
                    FROM GEG_Correspondance_Offre_Produit__c
                    WHERE Id IN :produitIds
                        AND Type_Energie__c = '0'
    ]) {
        produitWithZeroEnergie.add(produit.Id);
    }

    // Now create a lookup from account ID and contact ID combo to contract (the assumption is these are a
    // unique pair), but only for those contracts that have zero energie produit and are B2C record type
    Map<Object[], Contract> contractsByKey = new Map<Object[], Contract>();

    for (Contract contract : Trigger.new) {
        if (produitWithZeroEnergie.contains(contract.GEG_Correspondance_Offre_Produit__c) &&
                contract.RecordTypeId == b2c) {
            contractsByKey.put(new Object[]{contract.AccountId, contract.GEG_Contact_Titulaire__c},
                    contract);
        }
    }

    // Find all possible cases matching the accounts and contacts from the contracts and that are in the
    // right status and have a zero energie produit
    List<Case> cases = [
            SELECT Id, ContactId, AccountId
                    FROM Case
                    WHERE Status = 'Clôturée'
                            AND RecordType.Name = 'Demande de Souscription'
                            AND GEG_Correspondance_Offre_Produit__r.Type_Energie__c = '0'
                            AND AccountId IN :accountIds
                            AND ContactId IN :contactIds
    ];

    // Now we can easily work out when cases need updating because they match the contracts. Note that we
    // are only looking at cases that have zero energie and the corresponding contracts that have zero energie
    List<Case> casesToUpdate = new List<Case>();

    for (Case case : cases) {
        Contract contract = contractsByKey.get(new Object[]{ case.AccountId, case.ContactId });

        if (contract != null) {
            case.GEG_Contract_Requete__c = contract.Id;

            casesToUpdate.add(case);
        }
    }

    if (!casesToUpdate.isEmpty()) {
        update casesToUpdate;
    }
}

This code is based on your original trigger and the data bulkification patterns previously referenced.

1
1

There are at least 3 problems here.

Problem 1: Incorrect Logic

You have subsequently edited this change into your question, but your original logic was written with || instead of &&. This could have resulted in incorrect behavior because any matching criterion resulted in object association, instead of all criteria.

Problem 2: Failure to Query Correct Fields

You are accessing a number of fields on each Contract record:

con.AccountId == c.AccountId 
&& con.GEG_Contact_Titulaire__c == c.ContactId 
&& con.GEG_Correspondance_Offre_Produit__r.Type_Energie__c == c.GEG_Correspondance_Offre_Produit__r.Type_Energie__c

that you fail to query:

select Id , RecordType.Name from contract where Id in : ids

You must query all fields, including relationship fields, that you wish to reference.

You do the same thing with the relationship field you want on Case, but you don't need it - you should be comparing to a constant instead since you only want one value.

Problem 3: Failure to Bulkify

You indicated in a comment that you don't perceive the application here, but the section "I need data from indirectly related records based on one or more input record fields" of How do I bulkify queries for directly or indirectly related records? is in fact the pattern that you need.

Your current strategy is extremely inefficient because it iterates over every Case for every Contract in the trigger set. This can lead to performance problems up to and including CPU time limit exceptions. You need to use a composite key to efficiently access matching records, as described in the linked section.

4
  • Problem 1/2 are fixed . didn't understand the problem 3 you mentioned , the link you shared isn't helpful – Farkous Nov 2 '20 at 16:50
  • 1
    Respectfully, I think it's an excellent and helpful question and answer that demonstrates a very important and common problem on the platform. It is somewhat complex. But I'm not sure how to help you further when you don't provide any specifics of what you are struggling with or do not understand. – David Reed Nov 2 '20 at 16:53
  • @FirasT You are welcome here at any level - we're happy to help junior, senior, and anyone in between. I think the challenge you're seeing here is that you're often not providing the detail that somebody on the other side of a computer screen from you needs to start helping you with your problem. – David Reed Nov 2 '20 at 21:38
  • Here's a suggestion: try posting using the "Given/When/Then" formula. "Given": the data and setup that you start with; "when": you take a specific action; "then": the results that you get, and what is not right about them. This is a way to help yourself make sure you are sharing enough information with the person you are talking with. – David Reed Nov 2 '20 at 21:39

Not the answer you're looking for? Browse other questions tagged or ask your own question.