[SalesForce] Trigger for field update on Opportunity (child) records based on Account (parent) field value

I would like to automatically update the Stage field on all child opportunities to Closed Won when a custom checkbox field ("Payment Info on File") is checked on the parent account record.

I came close to achieving this functionality by modifying some helpful code provided by Keith C. on a similar thread (Trigger to update opportunity stage based on Account Status):

trigger moveToClosedWon on Account(after update)  {

Set<Id> accountIds = new Set<Id>();
for (Account acc : Trigger.new) {
     Account old = Trigger.oldMap.get(acc.Id);
     if (acc.Payment_Info_on_File__c = true && acc.Payment_Info_on_File__c != old.Payment_Info_on_File__c) {
         accountIds.add(acc.Id);
     }
}

if (!accountIds.isEmpty()) {
    List<Opportunity> opps = [
            select Id, StageName
            from Opportunity
            where AccountId in: accountIds
            and StageName != 'Closed Won'
            ];
    if (!opps.isEmpty()) {
        for (Opportunity opp : opps) {
            opp.StageName = 'Closed Won';
        }
        update opps;
    }
}
}

When I tested the trigger, I received the following error message:

Error:Apex trigger moveToClosedWon caused an unexpected exception, contact your administrator: moveToClosedWon: execution of AfterUpdate caused by: System.FinalException: Record is read-only: Trigger.moveToClosedWon: line 6, column 1

How can I accomplish my goal?

Best Answer

You could write you trigger logic like this:

trigger UpdateStageOnOpportunity on Account (after update) {
        set<Id>AccId = new set<Id>();
        for(Account Acc : trigger.new){
           if(Acc.Id != null && Acc.MyLightningP__Payment_Info_on_File__c == true){
               AccId.add(Acc.Id);
           }
        }

        map<Id,Opportunity>OppMap = new map<Id,Opportunity>([select Id, name, StageName 
                                                               from Opportunity
                                                              where AccountId in: AccId]);

        for(Account Acc : trigger.new){
            if(OppMap.Containskey(Acc.Id)){
                OppMap.get(Acc.Id).StageName='Closed Won';
            }     
        }
        update OppMap.values();
}