0

Disclaimer: This is my 2nd trigger ever as I work through the trailhead teachings. 2 questions: 1. how can I update the parent record since I already know the id of the account from the first

trigger sumContracts on CONTRACT (before update) {
for (Contract c : Trigger.new) {
    String id = '';

    // I Got the Account ID here

    List<Contract> acct = [SELECT Account.Id from Contract where Id =:c.Id];
    for(Contract a : acct){
        id = a.Account.Id;
    }

    //Get value of some fields

    AggregateResult[] groupedResults = [SELECT SUM(Total_Contract_Value__c) sum from Contract where ContractNumber='00000203'];

    //Covert it to a decimal, as a.Total_Contracts_Value__c is a currency field

    for (AggregateResult ar : groupedResults)  {
        String sum = String.valueof(ar.get('sum'));
        Integer intCheck = Integer.ValueOf(sum);
        Decimal decVal = Decimal.ValueOf(intCheck);


    // Get the Account Id again because I am new to apex

        List<Account> accounts = [Select ID, Name From Account WHERE ID=:id];
        for(Account a : accounts){

                //Update the Total_Contracts_Value__c field in the parent Account
                a.Total_Contracts_Value__c = decVal;    ///    <---- Fails

                // As a test, update the description of this record with the value we want
                c.Description = String.valueof(decVal) + "," + a.Name;  /// <---- Success
        }
    }
  }
}

****Edit***

Based on the response, I think I have removed the loops problem I was creating. I have successfully updated the parent record by doing so below. Please feel free to offer advice on anything you see.

trigger sumContracts on CONTRACT (after update) {
for (Contract c : Trigger.new) {
    String id = '';
    Decimal decVal;

    List<Contract> acct = [SELECT Account.Id from Contract where Id =:c.Id];
    for(Contract a : acct){
        id = a.Account.Id;
    }

    AggregateResult[] groupedResults = [SELECT SUM(Total_Contract_Value__c) sum from Contract where Account.Id=:id];

    for (AggregateResult ar : groupedResults)  {
        String sum = String.valueof(ar.get('sum'));
        Integer intCheck = Integer.ValueOf(sum);
        decVal = Decimal.ValueOf(intCheck);
    }
    //c.Description = String.valueof(decVal);

    Account myAccount = [Select ID, Name From Account WHERE ID=:id];
        myAccount.Total_Contracts_Value__c = decVal;
        update myAccount;
  }
 }
4
  • Are you getting errors? Could you please be a bit more explicit about what behavior you observe or what you mean by "fails"?
    – Adrian Larson
    Nov 6 '19 at 22:15
  • The accounts total contracts value field is not getting updated. When I do the test on contracts.description, that gets updated.
    – bart2puck
    Nov 6 '19 at 22:31
  • 1
    you are doing soql inside of a loop and your aggregate query doesn't make any sense as it is only looking at one record. I think you should use edit and define what business problem you are trying to solve
    – cropredy
    Nov 6 '19 at 22:43
  • You should consider looking at the variety of tools which make rollup summaries configurable across lookups, such as Declarative Lookup Rollup Summary (DLRS) or Rollup Helper.
    – Adrian Larson
    Nov 6 '19 at 22:48
2

There are a couple things in your code which need fixing.

  • Never consume any governors (such as queries, database operations, etc) in a loop.
  • Please read up on Triggers and understand what operations should be used when.

    You can define triggers for top-level standard objects that support triggers, such as a Contact or an Account, some standard child objects, such as a CaseComment, and custom objects. To define a trigger, from the object management settings for the object whose triggers you want to access, go to Triggers. There are two types of triggers:

    • Before triggers are used to update or validate record values before they’re saved to the database.
    • After triggers are used to access field values that are set by the system (such as a record's Id or LastModifiedDate field), and to affect changes in other records, such as logging into an audit table or firing asynchronous events with a queue. The records that fire the after trigger are read-only.
    • Given that you are trying to do a cross-object update, that logic belongs always in the after context.

When you are in the before context and you set fields on context records (trigger.new), those changes persist to the database automatically. If you would like to update any other record, it should go in the after context and you then need to perform a DML operation on those records.

switch on trigger.operationType
{
    WHEN AFTER_UPDATE
    {
        List<Account> relatedRecords = new List<Account>();
        // populate as needed

        update relatedRecords;
        // you should avoid "naked" DML operations like this
        // and instead wrap in a try/catch
        // make sure you NEVER use an empty catch block
        // and instead map the error back to the trigger context record which caused it
        // using the SObject.addError method
    }
}
1
  • Brilliant I will dig in further tomorrow. I truly appreciate the tesponse
    – bart2puck
    Nov 6 '19 at 23:35

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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