hoping someone can help me with this issue.

We have a requirement to migrate all .pdf attachments (purchase orders) from the Contract object in our current SFDC org. to the Account object in our new SFDC org.

Finding which attachments to migrate is simple enough:

SELECT Name, ID, ParentID
FROM Attachment
    ParentID IN (
        SELECT ID
        FROM Contract
    Name LIKE '%.pdf'

The problem I'm having is finding a way to place the related AccountID on the Attachment record, either by updating the ParentID or maybe even in the description field.

Basically we have:


and we want to move the Attachment to relate to the Account directly, but can only traverse one level up from Attachment.

I believe this stems from the fact that the Parent relationship on Attachment is polymorphic.

Does anyone know a way to achieve this in Salesforce?

If I can't find a way I guess I'll take the AccountID from the Contract record and use a Vlookup table in Excel to update them.

Thanks for any help


So I've come up with this class, and it works perferctly - except for it hitting governor limits. Could someone familiar with writing Batch Apex help me convert it to run in Batch please? I have zero experience with Batch Apex.

global class AttachmentReparentPDFsToAccount {
    webservice static string reparentPDFs () {

    // Set containing the Ids of the Accounts that will be migrated
        Set<Id> accountIdSet = new Set<Id> {};
        for (Account acc:[SELECT Id FROM Account WHERE (Type = 'Subscriber' OR Type = 'Ex-Subscriber' OR Type = 'Division' OR Type = 'EDU SUBSCRIBER')]){
            System.debug('No. of valid accounts: ' + accountIdSet.size());

        // Set containing the Ids of the Contracts that will be migrated
        Set<Id> contractIdSet = new Set<Id> {};
        for (Contract con:[SELECT Id FROM Contract WHERE AccountId IN :accountIdSet]){
                System.debug('No. of valid contracts: ' + contractIdSet.size());

        // Create the map that will contain ContractId => Contract.AccountId
        Map<Id,Id> contractToAccountMap=new Map<Id,Id>();

        // Lists of attachments to be created then deleted
        List<Attachment> attachmentsToDelete = new List<Attachment>(); 
        List<Attachment> attachmentsToCreate = new List<Attachment>(); 

        // Populate the map with the ContractId and AccountId related to the Contract
        for(Contract ctr:[Select Id,AccountId from Contract where Id in:contractIdSet]){

        // Attachment tempAtt = new Attachment();
        // Update the parentId of the attachment, changing from the ContractId to the related AccountId
        for(Attachment att:[SELECT Name, Id, Body, ParentId From Attachment WHERE ((Name LIKE '%.pdf') AND (ParentId IN :contractIdSet))]){
                Attachment tempAtt = new Attachment ( name = att.name, body = att.body, parentId = contractToAccountMap.get(att.ParentID));
                // tempAtt.ParentID = contractToAccountMap.get(att.ParentID);

        // Update the list of attachments
        insert attachmentsToCreate;
        delete attachmentsToDelete;
        System.debug('No. of attachments to be created; ' + attachmentsToCreate.size());

        // Return the no. of attachments that have been reparented
        String listSize =  String.valueOf(attachmentsToCreate.size());
        listSize += ' attachments have been reparented to their root Accounts.';


Thanks again!

  • If you are migrating from one org to another won't the account ids in the new org be different from the account ids in your old org in any case? Commented Oct 7, 2012 at 14:58
  • Extract and vlookup works best in my opinion. Commented Oct 8, 2012 at 19:08
  • @grigriforce we've included the account IDs as legacy IDs & external keys for use with Data Loader etc. Commented Oct 8, 2012 at 20:12

1 Answer 1


From what i understand we basically need to re-parent the Attachments with Account Id

First migrate all as it is in current Org to the new org.

Then we can write a small snippet piece in execute anonymous to do the re-parenting .

I drafted a simple algorithm and you may need to tweak a little to get it working based on realtionship you have

1)Construct a map of Contract Id and Account Id(Map)(Query Contract ) 2)Query all the attachments which you want to reparent with parent Id 3)From the parent Id (i.e Contract ) also using the map of Contract Id and Account Id you can obtain the Account Id 4)Update the attachments with parent Id equal to the attachment Id (Query and iterate through each to change the parent Id and collect in list and update the List ) 5)Must be written in execute anonymous and executed (Try in sandbox first) 6)Hope you have less no of attachments not exceeding 10,000 ROWS

Map<Id,Id> mapofAcccIdIdbyContractId=new Map<Id,Id>();
Set<Id> setparentIds=new Set<Id> parentIds();
List<Attachment> lstattch=new List<Attachment>(); 
for(Attachment att:[SELECT Name, ID, ParentID WHERE Name LIKE '%.pdf']){

for(Contract ctr:[Select Id,AccountId from Contract where Id in:setparentIds]){


 for(Attachment att:[SELECT Name, ID, ParentID WHERE Name LIKE '%.pdf']){





update lstattch;
  • Unfortunately we have over 30,000 attachments to be migrated, do you have any suggestions on how to proceed? Commented Oct 8, 2012 at 9:52
  • Thanks, look good but now I'm getting an error that Attachment.ParentId is not writable? Commented Oct 8, 2012 at 13:49
  • Hmmm i got the issue.The issue is while updating we cannot write into parent Id so you may need to do a delete call and then modify update to insert.Conclusion:First fetch everyting make a delete call.Then have a insert call in batch. Commented Oct 8, 2012 at 14:21
  • I came to the same conclusion. Thanks for all your help. Commented Oct 8, 2012 at 14:49
  • any ideas on how to get around the 1000 row limit on maps? Commented Oct 10, 2012 at 12:58

You must log in to answer this question.

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