5

as a follow up to my earlier question I am now getting BeforeUpdate trigger errors when trying to insert attachments as it says the ParentId field is not writable.

so here below is the code that is supposed to copy all the attachments for the record from my custom object to the new record on the demand object in the BeforeUpdate event that fires when the record is updated:

// copy attachments:
List<Attachment> insertingAttachments = new List<Attachment>();
List<Attachment> lstAttachments = [SELECT Id, Body, Name, LastModifiedDate, contentType, OwnerId from Attachment where ParentId =:rq.Id];

for (Attachment att : lstAttachments){
    Attachment  newattch = new Attachment();  
    newattch = att.clone(false);
    newattch.body = att.body; // required field
    newattch.name = att.name;// required field  
    newattch.OwnerId=att.OwnerId;                           
    newattch.parentId = newD.Id;
    insertingAttachments.add(newattch);                           
    }

if (insertingAttachments.size() > 0){
    insert   insertingAttachments;         
    }

// copy Notes:
List<Note> insertingNotes = new List<Note>();
List<Note> lstNotes = [SELECT Id, Title, ParentId, LastModifiedDate, createdbyid, body FROM Note where ParentId =:rq.Id];

for (Note eachNote : lstNotes){

    Note newDemandNote= new Note();
    newDemandNote.Body = eachNote.Body;                            
    newDemandNote.Title=eachNote.Title;    
    newDemandNote.Id = eachNote.Id;
    //newDemandNote.createdBy = string.valueOf(eachNote.CreatedById);
    //newDemandNote.createdBy = eachNote.OwnerId;
    newDemandNote.ParentId = newD.Id;

    insertingNotes.add(eachNote);                            
    }

if (insertingNotes.size() >0){
    insert insertingNotes;
    }

the error I am getting is:

Apex trigger System_Change_Request_Events caused an unexpected exception, contact your administrator: System_Change_Request_Events: execution of BeforeUpdate caused by: System.DmlException: Insert failed. First exception on row 0; first error: CANNOT_INSERT_UPDATE_ACTIVATE_ENTITY, attachment_before_insert: execution of BeforeInsert caused by: System.NullPointerException: Attempt to de-reference a null object Trigger.attachment_before_insert: line 39, column 1: []: Trigger.System_Change_Request_Events: line 310, column 1

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:

    List<Note> insertingNotes = new List<Note>();
    List<Note> lstNotes = [SELECT Id, Title, ParentId, LastModifiedDate, createdbyid, body FROM Note where ParentId =:rq.Id];

    for (Note eachNote : lstNotes){

        Note newDemandNote= new Note();
        newDemandNote.Body = eachNote.Body;                            
        newDemandNote.Title=eachNote.Title;    
        newDemandNote.Id = eachNote.Id;
        //newDemandNote.createdBy = string.valueOf(eachNote.CreatedById);
        //newDemandNote.createdBy = eachNote.OwnerId;
        newDemandNote.ParentId = newD.Id;

        insertingNotes.add(eachNote);                            
        }

        if (insertingNotes.size() >0){
            insert insertingNotes;
            }                                            

        // copy attachments:
        List<Attachment> insertingAttachments = new List<Attachment>();
        List<Attachment> lstAttachments = [SELECT Id, Body, Name, LastModifiedDate, contentType, OwnerId from Attachment where ParentId =:rq.Id];

        for (Attachment att : lstAttachments){
            Attachment  newattch = new Attachment();  
            newattch = att.clone(false);
            newattch.body = att.body; // required field
            newattch.name = att.name;// required field  
            newattch.OwnerId=att.OwnerId;                           
            newattch.parentId = newD.Id;
            insertingAttachments.add(newattch);                           
            }

            if (insertingAttachments.size() > 0){
                insert   insertingAttachments;         
                }

to clarify, line 310 is insert insertingAttachments;

UPDATE: here is the trigger attachment_before_insert on Attachment:

trigger attachment_before_insert on Attachment (before insert) {

if (Trigger.isBefore && Trigger.isInsert)
{
    PPTriggerUtils triggerUtils = new PPTriggerUtils();

    triggerUtils.handleAttachmentBeforeInsert(Trigger.new);
}

/*//Build list of Ids to be excluded from file check 
set<string> ExcludedIds = new set<string>();

//Project Object
for(apm2__Project__c p:[select Id, Name from apm2__Project__c]){
    ExcludedIds.add(p.Id);
}

//Project Task Object
for(apm2__Project_Task__c pt:[select Id, Name from apm2__Project_Task__c]){
    ExcludedIds.add(pt.Id);     
}

//Demand Object
for(apm2__Demand__c d:[select id, Name from apm2__Demand__c]){
    ExcludedIds.add(d.Id);          
}

//Change Control Object
for(Change_Control__c cc:[select id, Name from Change_Control__c]){
    ExcludedIds.add(cc.Id);         
}*/

for(Attachment a:trigger.new){

    //Set check attachment to true
    boolean CheckFileType = true;

    Id ids = a.ParentId;
    Schema.SObjectType ObjectName = ids.getSObjectType();

    if( string.valueOf(ObjectName) != 'Case'    && 
        string.valueOf(ObjectName) != 'Credit_Case__c' &&
        string.valueOf(ObjectName) != 'Loan_Application__c')CheckFileType = false;

    //Is user the API user?
    if( Userinfo.getUserName().contains('apiuser@bankofcyprus.co.uk') ||
        Userinfo.getUserName().contains('buser@bankofcyprus.co.uk')
        )CheckFileType = false;

    //If Checkfiletype equal true then check attachment contentType and then name. 
    if(CheckFileType == true){

        if(string.valueOf(ObjectName) == 'Case' || string.valueOf(ObjectName) == 'Credit_Case__c'){
            //Checks if a file is a ZIP file
            if(a.ContentType == 'application/x-zip-compressed'){
                a.adderror('ZIP files cannot be uploaded into Salesforce');
            }

            //Checks if a file is a HTML file
            if(a.ContentType == 'text/html'){
                a.adderror('HTM/HTML files cannot be uploaded into Salesforce');
            }


            //If you cannot check by contentType
            if(a.Name.contains('.')){
                list<string> sWrd = a.Name.splitByCharacterTypeCamelCase();
                integer i = sWrd.size()-1;

                if(i>=0){
                    string ext = sWrd[i];

                    //Check if file is Rich Text Format
                    if(ext.toLowerCase() == 'rtf')a.adderror('RTF files cannot be uploaded into Salesforce');

                    //Check if file is Csv Format
                    if(ext.toLowerCase() == 'csv')a.adderror('CSV files cannot be uploaded into Salesforce');

                    //Check if file is Csv Format
                    if(ext.toLowerCase() == 'msg')a.adderror('MSG files cannot be uploaded into Salesforce');

                    //Check if file is Csv Format
                    if(ext.toLowerCase() == 'zip')a.adderror('ZIP files cannot be uploaded into Salesforce');

                }
            }
        }
        else if(string.valueOf(ObjectName) == 'Loan_Application__c'){
            //Checks if a file is not a pdf file

            integer endP = a.Name.length();
            integer staP = endP - 4;

            if(a.name.substring(staP,endP).touppercase() != '.PDF'){            
                a.adderror('Only PDF documents can be uploaded');
            }
        }
    }
}

}

and line 39 is Schema.SObjectType ObjectName = ids.getSObjectType();

so it doesn't like the idea of an insert into the attachments table referencing a newly inserted record in the Demand object during a Before_Update...

so what am I missing here, surely I can copy the notes and attachments?

  • 5
    Line 39 is in a separate trigger: attachment_before_insert - that is where I would look first - when you clone these attachments, you'll fire the before insert trigger on the clones – cropredy Feb 18 '16 at 2:46
  • 1
    Checking !myList.isEmpty() before DML is unnecessary, but here it does tell us the list isn't null, or you would get your NullPointerException there. Error is definitely in the other trigger. – Adrian Larson Feb 24 '16 at 14:51
  • 1
    You need to post the entire code from both triggers involved. – Davin Casey Feb 25 '16 at 11:44
  • 1
    And are you sure ` insert insert insertingAttachments; ; ` this line is causing error. Because if insertingAttachments is null then before this line if (insertingAttachments.size() > 0) will give error. – Tushar Sharma Feb 25 '16 at 11:45
  • 1
    @OurManInBananas As mentioned by crop1645, if you want a proper answer, you need to include the trigger "attachment_before_insert" in your question-- this trigger code is perfectly fine. In fact, I've done code like this before. – sfdcfox Feb 25 '16 at 20:23
4
+50

As of now you can not transfer attachment from one object to another by just updating ParentId.

You will have to create/clone new attachment assign the clone to new parent and delete original copy.

List<Attachment> attachmentsToInsert = new List<Attachment>();

List<Attachment> attachmentsToDelete = new List<Attachment>();

Attachment tempAtt;


for(Attachment attachment: [
        SELECT SystemModstamp, ParentId, OwnerId, Name, 
            LastModifiedDate, LastModifiedById, IsPrivate, IsDeleted, 
            Id, Description, CreatedDate, CreatedById, 
            ContentType, BodyLength, Body 
        FROM Attachment 
        WHERE parentId=:'parentid']) {

    tempAtt=attachment.clone(false,false);
    tempAtt.parentId = quoteSelWrap1.quot.Id;

    attachmentstoInsert.add(tempAtt);
    attachmentsToDelete.add(attachment);

}

insert attachmentsToInsert;
delete attachmentsToDelete;

EDIT 1

After looking at Adrian Larson comment I realized that there is no issue in original code posted in question.

I tested it with some hard coded values and its just run fine on my dev org.

List<Attachment> insertingAttachments = new List<Attachment>();
List<Attachment> lstAttachments = [SELECT Id, Body, Name, LastModifiedDate, contentType, OwnerId from Attachment where ParentId ='5009000000SbT0s'];

for (Attachment att : lstAttachments){
    Attachment  newattch = new Attachment();  
    newattch = att.clone(false);
    newattch.body = att.body; // required field
    newattch.name = att.name;// required field  
    newattch.OwnerId=att.OwnerId;                           
    newattch.parentId = '5009000001D6dxd';
    insertingAttachments.add(newattch);                           
    }

if (insertingAttachments.size() > 0){
    insert   insertingAttachments;         
    }

So this issue is at due to error at line # 39 in attachment_before_insert trigger.

  • 1
    I don't think you understand the question. OP is inserting new records. – Adrian Larson Feb 24 '16 at 15:04
  • Yes, you were correct, the issue was caused by the lines Id ids = a.ParentId; and Schema.SObjectType ObjectName = ids.getSObjectType(); - as I had mistakenly commented the line newattch.ParentId = newD.Id; no ParentId was passed, throwing the System.NullPointerException: Attempt to de-reference a null object ! A similar error was on the Notes table, the error was in the line insertingNotes.add(eachNote); instead of the correct insertingNotes.add(demandNote); – Our Man in Bananas Feb 29 '16 at 15:22
3

This error indicates that you are trying to get the sObjectType of your ParentId when it is null. In other words, the new records you are cloning have a ParentId of null. Not sure where you are getting your newD record from, but if you're cloning it somewhere, it's possible you can resolve this issue simply by cloning with preserveId=true. For example:

system.assertEquals(null, someD.clone(/*preserveId*/ false), 'Blank Id');
system.assertNotEquals(null, someD.clone(/*perserveId*/ true), 'Populated Id');

If the Id is blank because you have not yet inserted newD, then you must insert the records first, before adding attachments to them.

In the long run, if you can redesign the Attachment trigger, you probably should. I find it pretty frustrating to have field requiredness enforced by faulty triggers with a NullPointerException. If you move the inner block of your for loop into another method, then you can immediately return if the ParentId is null. Otherwise, work a guard clause in there using the continue statement.

  • Thanks Adrian, yes, the ParentId was noty being passed for the attachment table, and in the Notes part, I had a bug in my insert notes code... – Our Man in Bananas Feb 29 '16 at 15:23
  • I just wish I could successfully lock the record now, but that's another question, and another headache! :) – Our Man in Bananas Feb 29 '16 at 15:24
  • @OurManInBananas Are you getting our answers confused? – Adrian Larson Feb 29 '16 at 15:34
  • yes, probably :), thanks for your answer, I will pass this advice on about that Attachment trigger – Our Man in Bananas Feb 29 '16 at 15:37
  • 1
    sorry but I awarded the bounty just before your answer showed up...in fact the part of his answer that really helped me was So this issue is at due to error at line # 39 in attachment_before_insert trigger which woke me up to the fact that I was not passing the ParentId of the new record in the Attachments table...also, he showed an example of code successfully inserting a record in that table. I am sorry if it looked like I had used your answer and given the credit elsewhere – Our Man in Bananas Feb 29 '16 at 15:59

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.