0

I am updating a large amount of opportunity line items. We have a margin text field and when our costs for the product changes, we need this text field to change. We have a formula field but we have to see the margin when in multi line edit. The issue that I am having is that there are summary fields on Account from Opps and Opps from Opp line items. So when I do the update, the account is locked from the latest update. Although the summary field amount is not changing, I suppose there is a process to double check and lock the record. Does anyone know how to get around this. I am posting my code below.

I have read the previous posts to the same question and For Update does not work even if I use for accounts.

Trigger:

trigger MasterProductTrigger on Product2 (
before insert, after insert, 
before update, after update, 
before delete, after delete) {

    if (Trigger.isBefore) {
        if (Trigger.isInsert) {

        } 
        if (Trigger.isUpdate) {
        }
        if (Trigger.isDelete) {
          // Call class logic here!
        }
    }

    if (Trigger.IsAfter) {
        if (Trigger.isInsert) {
        } 
        if (Trigger.isUpdate) {

            ProductChanges checker = 
    new ProductChanges(Trigger.oldMap, Trigger.newMap);
            checker.LandingCostChange();

            if(TriggerContextUtility.isFirstRun()){

            }

        }
        if (Trigger.isDelete) {
            // Call class logic here!
        }
    }
}

Helpers:

public class ProductChanges{
    // These variables store Trigger.oldMap and Trigger.newMap
    Map<Id, Product2> oldProds;
    Map<Id, Product2> newProds;

    List<Product2> acctsToUpdate = new List<Product2>();
    // This is the constructor
    // A map of the old and new records is expected as inputs
    public ProductChanges(Map<Id, Product2> oldTriggerProds, Map<Id, Product2> newTriggerProds) {
        oldProds = oldTriggerProds;
        newProds = newTriggerProds;
    }

    // The one method your master trigger will call
    public void LandingCostChange() {

        //get a new list of products that the landing costs have changed

        List<Product2> plandchange = new List<Product2>();
        for(Product2 p : newProds.values()){

            Product2 o  = oldProds.get(p.Id);
            if(o.Cost__c != p.Cost__c && p.IsActive == true){

                plandchange.add(p);

            }
        } 

        //send product list to future method so that the oppline items that need changed will be changed. 
        String jsonString = json.serialize(plandchange);
        ProductLandingCosts.UpdateOppLines(jsonString);
    }
}

global class ProductLandingCosts{
    @future
    public static void UpdateOppLines(String jsonString) {

        //set list for  the products that landing costs had a change
        List<Product2> prods = 
(List<Product2>)Json.deserialize(jsonString,List<Product2>.class);

        //query all the oppline items that have matching products. and sort by 
        //account and then opportuntity to avoid
        //lock records issue
        List<OpportunityLineItem> lines = [Select Id,OpportunityId, 
  Opportunity.AccountId,Margin__c,MarginText__c,
                Landing_Cost2__c,Product2Id, Product2.Cost__c from 
  OpportunityLineItem where Product2Id In: prods 
                ORDER BY Opportunity.AccountId,OpportunityId ];
        //set a list for update
        List<OpportunityLineItem> lineitems = new List<OpportunityLineItem>();

        line.MarginText__c = String.valueOf(line.Margin__c)+'%';
        Line.Landing_Cost2__c = Line.Product2.Cost__c;
        lineitems.add(Line);
    }

    TriggerContextUtility.setFirstRunFalse();

    update lineitems;
}

1 Answer 1

3

You have use FOR UPDATE in SOQL query to lock sObject records while they’re being updated in order to prevent race conditions and other thread safety problems.

While an sObject record is locked, no other client or user is allowed to make updates either through code or the Salesforce user interface. The client locking the records can perform logic on the records and make updates with the guarantee that the locked records won’t be changed by another client during the lock period. The lock gets released when the transaction completes.

To lock a set of sObject records in Apex, embed the keywords FOR UPDATE after any inline SOQL statement.

For more information, refer Locking Statements

Update based on comments

Whether records are locked or not, you can check using Approval.islocked()

public static Map<Id,Boolean> isLocked(List<SObject> sobjects)

Refer Approval methods

9
  • for update on the opportunitylineitems? How would that help for the Accounts and Opportunities? Apr 14, 2017 at 18:56
  • I tried it and still getting the error. The debug log shows the lock error is on the Account, not the line items. I tried the lock the account rows using a sql statement but that caused an error saying that the Account was locked. Apr 14, 2017 at 19:04
  • The account is not in approval. I think the summary fields are the culprit that are locking the account records, as I am not updating accounts, only opplineitems. Here is the debug statement. Update failed. First exception on row 0 with id 00kA000000CqkDhIAJ; first error: UNABLE_TO_LOCK_ROW, unable to obtain exclusive access to this record or 89 records: ...... then there is a list of 89 accounts. Apr 14, 2017 at 19:43
  • normally if we have rollup summary, or sharing rule then when records are updated then parents records are locked for sometime for calculation or sharing. Secondly, records are not locked here for approval. Approval class has method to lock/unclock or checking if record is locked (without submitting the approval request) Apr 14, 2017 at 19:58
  • "normally if we have rollup summary, or sharing rule then when records are updated then parents records are locked for sometime for calculation or sharing" ... So how do I update opplines ? do i group by account, or wait for them to be updated or can I just break through the lock? Apr 14, 2017 at 20:06

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .