how to addError() in class and how to test it

  • How to use adderror() in a class. My scenario is, I have and child object on Opportunity, Payment__c. I want to write validation on opportunity(trhough apex) that if Payment__c.confirmation_status__c != 'Confirmed' user can not closed-won Opportunity. I have written the class and error is

    "SObject row does not allow errors: Class.IsPaymentConfirmed.closedWonOpp".

    ( I have successfully added and implement .adderror in trigger but will be happy if I can have it in class)
    1. trigger

     if(trigger.isUpdate ||
        trigger.isBefore||
        trigger.isInsert){
        system.debug('--isB--' + trigger.isBefore + '--isU--'+ trigger.isUpdate+ '--isA--'+ trigger.isAfter);
    
        for(Opportunity tempOpp : trigger.new){
            if(tempOpp.StageName =='Closed Won'){
                system.debug('-to check whether payment is confirmed or not before closed won-'+ tempOpp.StageName);
                Opportunity oppInLight = trigger.oldMap.get(tempOpp.id);
                IsPaymentConfirmed ipc = new IsPaymentConfirmed();
                ipc.closedWonOpp(trigger.new);
           }
        }
    }
    
    //2. class
    public void  closedWonOpp(list<Opportunity> tempOpp){
    system.debug('--Stage--'+ tempOpp[0].StageName + '--Opp ID--'+ tempOpp[0].id);
     list<Opportunity> paymentConfirmed = [SELECT id,stageName,
                                                                           (
                                                                              SELECT Salesforce_No__c, Confirmation_status__c 
                                                                              from Payment_Details__r where confirmation_status__c != 'Confirmed'
                                                                           )
                                                                         From Opportunity
                                                                             where
                                                                       id =: tempOpp[0].id];
       system.debug('--Oppotunity Detail--' + paymentConfirmed);
       for(Opportunity confirmed : paymentConfirmed){
          for(Payment_Detail__c tempPD : confirmed.Payment_Details__r){
              system.debug('--is it coming here--' + tempPD);
              if(tempPD.Confirmation_Status__c != 'Confirmed'){
                   system.debug('-Opp. Stage-'+confirmed.StageName+'-PD Confirmation Status-'+tempPD.Confirmation_Status__c);
                   confirmed.StageName.addError('Please let the Payment Confirmed before Closed Won');
                   checkingPayment.add(confirmed);
              }
           }
       }
       insert checkingPayment;
    

    Any help will be appreciated. Thanks for reading and showing interest.

    Can I access child object without SOQL like this :for(Opportunity confirmed : paymentConfirmed **'//will be trigger.new)'** { for(Payment_Detail__c tempPD : confirmed.Payment_Details__r){ system.debug('--is it coming here--' + tempPD); if(tempPD.Confirmation_Status__c != 'Confirmed'){ system.debug('-Opp. Stage-'+confirmed.StageName+'-PD Confirmation Status-'+tempPD.Confirmation_Status__c); confirmed.StageName.addError('Please let the Payment Confirmed before Closed Won'); checkingPayment.add(confirmed); } } }

    No, the trigger will only contain the opportunity records, not the full object graph of related records.

  • Bob Buzzard

    Bob Buzzard Correct answer

    7 years ago

    The problem here is that you can't execute the addError method on records that are not part of the trigger context. You are passing the opportunities from the trigger to your class method, but then you query the first element of the array from the database, including related objects, and store it in the paymentConfirmed property. You then execute the addError method on paymentConfirmed, which is a different sobject to that in the trigger context.

    As you are only processing the first element of the array of opportunities from the trigger, you can simply add the error to that instead, so change:

    confirmed.StageName.addError('Please let the Payment Confirmed before Closed Won');
    

    to

    tempOpp[0].StageName.addError('Please let the Payment Confirmed before Closed Won');
    

    I'm also duty bound to point out that your class is only processing the first element in the array from the trigger, and there could be up to 200 elements, so you should look to bulkify this so that it can handle multiple records.

    I also don't think you should have the final line in the class method:

    insert checkingPayment;
    

    as the opportunity records are already in the process of being saved to the database through the trigger. I suspect this line will cause recursion, as the insert will fire the trigger again, which will cause another insert ad infinitum.

    Thanks a billion Bob. I am always your admirer and now you proved me right. It has been solved and I also have rectified all the concern you pointed out. Thanks again.

    Hi Bob, Can you please let me know how can I write test class for the above code. It is an request.

License under CC-BY-SA with attribution


Content dated before 7/24/2021 11:53 AM

Tags used