6

I need to lock a record in my trigger after a member of the systems team has changed it.

I tried this code:

// Lock the request
Approval.lock(rq.Id); 

rq is the record in my custom object.

The error I get is:

Apex trigger System_Change_Request_Events caused an unexpected exception, contact your administrator: System_Change_Request_Events: execution of BeforeUpdate caused by: System.UnexpectedException: null: Trigger.System_Change_Request_Events: line 315, column 1

line 315 is the one above that tries to lock the record.

UPDATE - here is the trigger code for isUpdate:

if (Trigger.IsUpdate && trigger.isbefore)
{
system.debug('(Trigger.IsUpdate && trigger.isbefore) BLOCK');
for (Request_for_System_Change__c rq : Trigger.new) {

system.debug('"for (Request_for_System_Change__c rq : Trigger.new) {" BLOCK');               

// Access the "old" record by its ID in Trigger.oldMap
Request_for_System_Change__c oldrecord = Trigger.oldMap.get(rq.Id);

system.debug('step 4 Record Type=' + oldrecord.RecordType.Name);

system.debug('in trigger get LineManagerName');
string lineManagerName = [SELECT Name from User where Id =: rq.Manager__c].Name;
system.debug('in trigger Line Manager name for Demand: ' + lineManagerName);           

if (rq.RecordTypeId == systemChangeRecordType)
    {           
if (rq.Status__c != OldRecord.Status__c && rq.Status__c == 'Convert to Demand') // && rq.RecordTypeId == systemChangeRecordType )
                {

rq.Skip_Validation__c=true;
system.debug('trigger CONVERT TO DEMAND step 2a SYSTEM CHANGE REQUEST - Approved and create demand for '+ systemChangeRecordType);

apm2__Demand__c newD = new apm2__Demand__c();
//+ oldrecord.RecordType.Name + '
newD.Demand_request_date__c = rq.Date__c;
newD.Project_Department__c = 'Systems';
newD.Demand_Title__c = rq.Request_Title__c;
newD.apm2__Criticality__c = rq.Request_Priority__c;
newD.Demand_Requested_by__c = lineManagerName;
newD.Prioritisation_points__c = rq.Benefits_Reasons__c;
newD.Demand_request_date__c = system.today();
newD.apm2__Business_Driver__c = rq.Benefits_Reasons__c;
newD.Demand_Status__c = 'New';
newD.Originating_Request__c=rq.Id;
                    newD.Assigned_Resource__c=rq.Assigned_resource_for_demand__c;
    newD.apm2__Demand_Type__c = 'Enhancement Request';

    // rq.Description_of_change_requested__c.length()>=149 ? rq.Description_of_change_requested__c.substring(0,150) :rq.Description_of_change_requested__c    
    string sDesc;
    integer iLen = rq.Description_of_change_requested__c.length();    

    if (iLen>=149){
      sDesc= rq.Description_of_change_requested__c.substring(0,150);
        }else     {sDesc= rq.Description_of_change_requested__c;}

    newD.apm2__Description__c = ' Systems Change Request  (' + oldrecord.Name + ') \r\n' + sDesc +' ... (see Request for full description)' ;
    newD.Extended_Description__c = 
    rq.Description_of_change_requested__c + ' \r\nExpected outputs:\r\n ' + rq.Expected_outputs__c
         + ' \r\nLegal / Regulatory / Compliance impact:\r\n ' + rq.Legal_Regulatory_Compliance_impact__c    ;
    newD.Additional_Information__c = rq.Anticipated_benefits_reason_for_change__c + ' \r\n' + rq.Additional_information_comments__c;
    newD.Department_Requested_by__c = rq.Department_Name_String__c;
                newD.Systems_Affected__c=rq.Systems_Affected__c;

    insert newD;
    rq.Demand__c = newD.Id;
    rq.Demand_Name__c=[select name, id from apm2__Demand__c where Id=:newD.Id].Name; //newD.Name;

    system.debug('New demand ID: ' + newD.Name);
    rq.Status__c='Converted';       

    system.debug('Status: ' + rq.Status__c);
    system.debug('Demand: ' + rq.Demand__c);

    // now lock the record to prevent tampering...                                          // Lock the request
    // Lock the request
    // Approval.LockResult lock(Id recordId) 
    System.ApprovalProcess Approval=new System.ApprovalProcess();    
    // Approval.lock(rq.Id);
    Approval.LockResult =  Approval.lock(rq.Id); 

    // Lock the request
    //Approval.lock(rq.Id);

    // copy Notes:
    ...

It saved the changes ok, so the method signature must be correct.

I need to lock the record as once this part of the process has been reached there should never be any need to edit it.

But it has to be unlocked after it has been approved by the Line manager in order that systems can convert the request into a demand (by changing the status which kicks off the BeforeUpdate trigger).

in the Process Automation Settings this has been enabled

So what am I missing here?

5
  • 1
    Did you enable the feature?
    – Adrian Larson
    Feb 18, 2016 at 15:44
  • 1
    yes I enabled it Feb 18, 2016 at 16:05
  • 1
    Did you tried isSuccess and getError methods as shown here? developer.salesforce.com/docs/atlas.en-us.apexcode.meta/… to get some more details? Feb 18, 2016 at 16:22
  • 4
    It's not clear from the sparse amount of code you've supplied exactly what the source of the records is that you're operating on. Are they from a query, trigger.new, trigger.old or some kind of combined manipulation? With more code, you might get a better answer. I don't think you've provided enough for anyone to reproduce your problem or give you the answer you want.
    – crmprogdev
    Feb 24, 2016 at 18:06
  • @crmprogdev: yes, I have added the code or the trigger isUpdate Feb 29, 2016 at 13:49

2 Answers 2

5
+50

Judging by the exception, I'd say that the problem is that you are running this code on an older version of the api that doesn't support the Approval.lock() method. In a different post, I found that for some reason the code will compile and gives a runtime error.

If for some reason the lock methods weren't intended to work on trigger context variables, the fact is that (at least for now) they do. (Again assuming you are using the correct api version). The following simple example works fine for me:

trigger Account on Account (before update) {
    Approval.lock(trigger.new);
}

Note that I also added a Workflow Rule to the Account object in my org that always runs. It doesn't perform any actions, but it makes sure that the lock works consistently.

./workflows/Account.workflow

<?xml version="1.0" encoding="UTF-8"?>
<Workflow xmlns="http://soap.sforce.com/2006/04/metadata">
    <rules>
        <fullName>True</fullName>
        <active>true</active>
        <description>Always runs</description>
        <formula>TRUE</formula>
        <triggerType>onAllChanges</triggerType>
    </rules>
</Workflow>
2
  • so I should use System.ApprovalProcess Approval=new System.ApprovalProcess(); and Approval.lock(trigger.new to lock the record, or just Approval.lock(trigger.new ? Feb 29, 2016 at 15:30
  • 1
    @OurManInBananas When you want to lock the record initially, you should be fine using just Approval.lock(). Later when you want to unlock the record, you should be able to handle that with a regular approval process.
    – martin
    Feb 29, 2016 at 16:58
1

There are many possible reason. Like @martin suggest check the API version It must be compatible with(V35). MAybe you are running an older version.

As per Docs

Salesforce admins can edit locked records. Depending on your approval process configuration settings, an assigned approver can also edit locked records. Locks and unlocks that are set programmatically use the same record editability settings as other approval-process locks and unlocks. Record locks and unlocks are treated as DML. They’re blocked before a callout, they count toward your DML limits, and if a failure occurs, they’re rolled back along with the rest of your transaction. To change this rollback behavior, use an allOrNone parameter.

This is count as DML and you can't perform DML on trigger.new Not allowed. A runtime error is thrown.So this may be also a case. So I think you move the code in after update and then query record and perform DML. May be it will solve your problem.

2
  • 2
    Actually, Approval.lock() is allowed in triggers. There is no problem with doing it before update either; the changes you are updating will commit by the way. The one you have to look out for is the before insert trigger which will fail with a runtime exception of: System.ListException: Missing id at index: 0 . Assuming you are using the right api version of course.
    – martin
    Feb 25, 2016 at 13:24
  • 1
    @martin Also the lock record count as DML. with trigger.new it will also give exception. Feb 25, 2016 at 16:26

You must log in to answer this question.

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