+ Start a Discussion
Rajat MahajanRajat Mahajan 

Reg: Cant Update a read only record in trigger (After insert operation) - Please help - Urgent

Hi All, 


We are trying to connect Account to a custom object Purchase Order in a One to Many Relation. A custom field called account number will be auto populated in Purchase Order from Account object. But while saving the record we are getting a "Cant update a read only record" exception.


Had read some more posts on this exception, but we were not clear on that.


The code is :


trigger IntegratePOtoAccount on Purchase_Order__c (after insert, after update) {

//Declare Variables
public Map<Id,Id> poAccountId = new Map<Id, Id>();
public Map<Id,String> accIDAccNumber = new Map<Id, String>();
public List<Account> accountList = new List<Account>();
public List<Purchase_Order__c> poList = new List<Purchase_Order__c>();
public Purchase_Order__c po_rec;
public Id accountId;
public String accountNumber;

    //Fetch the account id from Purchase Order
    for(Purchase_Order__c po : trigger.new) {
    System.debug('po is'+po.Id);
    System.debug('po is 2'+po.Account__c);

    //Query all records of account to create a map
    accountList = [Select Id, name, account_number__c from account];

    //Put all the accounts in a map from Id to account  number
    for(Account acc : accountList) {
        accIDAccNumber.put(acc.Id, acc.account_number__c);

    //Insert the Account Number for each PO
    for(Purchase_Order__c po : trigger.new) {
        accountId = poAccountId.get(po.Id);
        accountNumber = accIDAccNumber.get(accountId);
        po.oracle_account_number__c = accountNumber;



Could you please let us know how to tackle this. we get an error at the last line po.oracle_account_number__c = accountNumber;


Thanks in Advance!


Cyril CatonCyril Caton

Hi Rajat,


you may want to put the items from Trigger.new in a separate list by querying the PO table and loop through that list.

The code below has not been tested, let me know if it worked for you!







//Getting the list of Purchase Orders getting updated
posToUpdate = [Select Id, oracle_account_number__c From Purchase_Order__c Where Id IN : Trigger.newMap.keySet()];


//Going through the list of POs to update
for(Purchase_Order__c po : posToUpdate) {
    accountId = poAccountId.get(po.Id);
    accountNumber = accIDAccNumber.get(accountId);
    po.oracle_account_number__c = accountNumber;


//Updating the objects
update posToUpdate;

Rajat MahajanRajat Mahajan

Hi Cyril,


We did try to take it in a list and update it later, but it does not work..





Hi Rajat


1. why not make oracle_account_number__c a formula field? like - Account__r.account_number__c (should it be editable?)


2.My basic rule is - if you are going to assign values to a record being insert/updated..., do it in a before trigger. what happens is that you "hitch a ride" on the already occuring DML (insert/update...) and assign the values you want.


so, if you do end up with a trigger, it should look like:

trigger IntegratePOtoAccount on Purchase_Order__c (before insert, before update) {

    //Create a set of account Ids
    set<Id> accIds = new set<Id>();
	List<Purchase_Order__c> pos = new List<Purchase_Order__c>();
    //Fetch the account id from Purchase Order
    for(Purchase_Order__c po : trigger.new)
		//lets check if the record has an account id - 
		//this is in case it is not mandatory field.
		// if its not mandatory and you end up having null values in your query for accounts, 
		//you get really bad performance when scaling up + there is no account to take the value from, so why bother?
		if(po.Account__c != null)
			//check if it is an insert - in which case we have to roll down the value for the first time (we already know we have an account value in the record
			//	OR
			//if its an update and the PO changed accounts - in which case we have to reflect the account_number__c of the new account
			if(trigger.isInsert || (trigger.isUpdate && po.Account__c != trigger.oldMap.get(po.Id).Account__c)
				//add the record account__c value to a set in case we havent inserted it already while iterating another PO that was inserted/updated for that account
				//keep a list of purchase orders that passed the filter and therefore are relevant - this way you wont have to iterate over
				//the entire trigger list afterwords (good for governor limits and performance)
	//basically checks if we encountered POs that passed our filters - in which case we need to retreive their accounts and update them respectively
		//Query for the relevant accounts. you dont need to query for every account in your org
		//use the below syntax to get back a map of account id to account object
		map<Id,Account> accs = new map<Id,Account>([Select Id,account_number__c from Account__c where Id IN: accIds]);
		//iterate the relevant purchase orders and assign the value of their repective account to oracle_account_number__c
		for(Purchase_Order__c po : pos)
			po.oracle_account_number__c = accs.get(po.Account__c).account_number__c;





And please mark it as resolved if it solves you problem - could help other people encountering the same problem.



You are attempting to update the same record, in an AFTER trigger, that the trigger was fired on. You can't do that.


Instead, change this to a BEFORE trigger, and simply set the value as needed. The trigger will handle the update for you.


Hope this helps,

Rajat MahajanRajat Mahajan
Hi John, If i make it as a before insert or update operation, then i am unable to fetch accountid, it returns null. Regards Rajat

Hi Rajat


1. did my previous post not help you? you have the trigger completely written there.


2.what do you mean cannot retrieve account id? from the purchase order? do you assign account id in a different trigger? if so, it may be order of operations that you are encountering.