+ Start a Discussion
Ryan GreeneRyan Greene 

upload attachments to list and save respectively

Hello,
I have an embedded VF Page in my Leads which pulls a custom onbject Documents, and displays all Documents in the list as below:
User-added image
You can probably tell what I'd like to do is upload a file to 'Proposal' and another one to other doc types I do not have listed there, and then click the 'Save All Docs Attached' button and have the attachments save properly to eash Document Type which is a separate record. Right now when I click Save it saves the attachment to each of the Doc Types in the list. Is there a way I can save the Attachment to a List, then have the List save to each respective Doc Type?
Apex:
public with sharing class LeadDoc{
    public ApexPages.StandardController Controller;
    public LeadDoc(ApexPages.StandardController Controller){
        this.Controller = Controller;}
    public Id getid {get;set;}{
        getid = ApexPages.currentPage().getParameters().get('id');}
    public List<Lead> Lds {get;set;}
    public List<Document__c> Docs {get;set;}
    public String fileName {get;set;}
    public transient Blob fileBody {get;set;}
    
    public LeadDoc(){
        Lds = [SELECT Id, Name FROM Lead WHERE Id =: getid];
        Docs = [SELECT Id, Name,Count_Attachments__c,Attach_Request_Status__c, Document__c,View_Doc__c,
                Lead__c FROM Document__c WHERE Lead__c =: Lds[0].Id];
    }
    public PageReference Save(){
        //List<Attachment> att = fileBody;
        for(Document__c doc : Docs){
            if(fileBody!=null){
                Attachment attachment = new Attachment();
                attachment.body = fileBody;
                attachment.name = this.fileName;
                attachment.parentId = doc.Id;
                insert attachment;}
        }
        return null;
    }
}

 
MandyKoolMandyKool
Based on my understanding, it seems like you want to store attachments based on Document Types.
Eg. If you have say 3 different types of documents
Say "Proposal", "Quote", "Pricing" then once user clicks on "Save All Docs Attached"
then those docs should be saved under those document type records.

If above understanding is correct, then you can use Map to store the documents based on document types, (Map<String, Document__c> mapDocsByTypes = new Map<String, Document__c>()) and use it to save the attachments to respective document types.

Hopes this helps!

 
Ryan GreeneRyan Greene
Very good thought on the Map, I hardly ever use Maps so I think I'm doing something wrong. I put the Map in but when I click Save it still saves one attachment to each Doc Type. Any suggestions on fixing the map?
public PageReference Save(){
        Map<Document__c,Blob> mapDocsByTypes = new Map<Document__c,Blob>();
        for(Document__c doc : Docs){
				mapDocsByTypes.put(doc,fileBody);
                Attachment attachment = new Attachment();
                attachment.body = mapDocsByTypes.get(doc);
                attachment.name = this.fileName;
                attachment.parentId = doc.Id;
                insert attachment;
        }
Thank you!
Ryan
MandyKoolMandyKool
Hi Ryan,

Can you please post your complete VF page code and Controller and provide couple of sample scenarios.
Looking at your code I feel you are querying Document records associated with lead and showing it to on embeded VF page.
But if you save all the attachments against each document, then next time when you go to leads page you will pull back lot of records.
 
Ryan GreeneRyan Greene
Hi Mandy,
Yes, it is an embedded VF Page in the Lead. I query all "Documents" records related to the Lead. A better screenshot than before below too.
Full VF Code: It really is only 16 lines!
<apex:page Controller="LeadDoc" tabStyle="Lead" lightningStylesheets="true">
    <apex:form >
        <apex:pageBlock >
            <apex:pageBlockButtons >
                <apex:commandButton value="Save All Docs Attached" action="{!Save}"/>
            </apex:pageBlockButtons>
        <apex:pageBlockTable value="{!Docs}" var="d">
            <apex:column value="{!d.Document__c}"/>
            <apex:column value="{!d.Attach_Request_Status__c}"/>
            <apex:column headerValue="File Input"><apex:inputFile value="{!fileBody}" filename="{!fileName}"/></apex:column>
            <apex:column value="{!d.View_Doc__c}"/>
            <apex:column value="{!d.Count_Attachments__c}"/>
        </apex:pageBlockTable>
    </apex:pageBlock>
    </apex:form>
</apex:page>
Lead screenshot:
User-added image
So in the above example I'd like to attach 'agreement.csv' to the A/R Aging record, and 'Book1.xlsx' to the Balance Sheet record upon clicking Save All Docs Attached. Currently it will only take the 'agreement.csv' file and attach it to all of the records displayed.
Current Apex Class:
public with sharing class LeadDoc{
    public ApexPages.StandardController Controller;
    public LeadDoc(ApexPages.StandardController Controller){
        this.Controller = Controller;}
    public Id getid {get;set;}{
        getid = ApexPages.currentPage().getParameters().get('id');}
    public List<Lead> Lds {get;set;}
    public List<Document__c> Docs {get;set;}
    public String fileName {get;set;}
    public transient Blob fileBody {get;set;}
    
    public LeadDoc(){
        Lds = [SELECT Id, Name FROM Lead WHERE Id =: getid];
        Docs = [SELECT Id, Name,Count_Attachments__c,Attach_Request_Status__c, Document__c,View_Doc__c,
                Lead__c FROM Document__c WHERE Lead__c =: Lds[0].Id];
    }
    public PageReference Save(){
        Map<Document__c,Blob> mapDocsByTypes = new Map<Document__c,Blob>();
        
        for(Document__c doc : Docs){
				mapDocsByTypes.put(doc,fileBody);
                Attachment attachment = new Attachment();
                attachment.body = mapDocsByTypes.get(doc);
                attachment.name = this.fileName;
                attachment.parentId = doc.Id;
                insert attachment;
        }
        return null;
    }
}
Maybe I'm running the for loop incorrectly?
MandyKoolMandyKool
This makes things clear. As you need to have attachment for each row, you need fileBody and fileName for each document record.
In your previous code you were using only one fileBody and hence it was giving you one last uploaded documents body.
Using wrapper class you can club different data points together and use it on your screen. This also gives you access to each individual wrapper record, which you can use to perform DML.

I wrote a sample code below, maybe you can use it and build on top of it.
<apex:page Controller="LeadDoc" tabStyle="Lead" lightningStylesheets="true">
    <apex:form >
        <apex:pageBlock >
            <apex:pageBlockButtons >
                <apex:commandButton value="Save All Docs Attached" action="{!Save}"/>
            </apex:pageBlockButtons>
        <apex:pageBlockTable value="{!Docs}" var="d">
            <apex:column value="{!d.recDoc.Document__c}"/>
            <apex:column value="{!d.recDoc.Attach_Request_Status__c}"/>
            <apex:column headerValue="File Input"><apex:inputFile value="{!d.fileBody}" filename="{!d.fileName}"/></apex:column>
            <apex:column value="{!d.recDoc.View_Doc__c}"/>
            <apex:column value="{!d.recDoc.Count_Attachments__c}"/>
        </apex:pageBlockTable>
    </apex:pageBlock>
    </apex:form>
</apex:page>
Here's controller (Note: As you are not using extension I removed the associated constructor from my code)
public with sharing class LeadDoc{
    private Id getid {get;set;}
    public List<DocumentWrapper> Docs {get;set;}
    
    public LeadDoc(){
        
        Docs = new List<DocumentWrapper>();
        
        //Get lead id from page url
        getid = ApexPages.currentPage().getParameters().get('id');
        if(getid != null){
            initPage();
        }
        
    }
    
    public void initPage(){
        for(Document__c recDoc : [SELECT Id, Name,Count_Attachments__c,Attach_Request_Status__c, Document__c,View_Doc__c,
                                         Lead__c FROM Document__c WHERE Lead__c =:getid]){
            DocumentWrapper recWrapper = new DocumentWrapper();
            recWrapper.recDoc = recDoc;
            recWrapper.fileName = '';
            recWrapper.fileBody = null;
            
            Docs.add(recWrapper);
        }
    }
    
    public PageReference Save(){
        
        List<Attachment> lstAttachments = new List<Attachment>();
        for(DocumentWrapper recWrapper : Docs){
            
            Attachment attachment = new Attachment();
            attachment.body = recWrapper.fileBody;
            attachment.name = recWrapper.fileName;
            attachment.parentId = recWrapper.recDoc.Id;
            lstAttachments.add(attachment);
        }
        
        try{
            insert lstAttachments;
        }catch(Exception ex){
            //Show exception to user
        }
        return null;
    }
    
    public class DocumentWrapper{
        public Document__c recDoc{get;set;}
        public string fileName{get;set;}
        public transient Blob fileBody{get;set;}
    }
}

In above code, you can see how different data pieces are clubbed together using "DocumentWrapper" and how VF code is modified to use it.
NOTE: If you try to load large files, you might get into view state issues, so you can think of restricting user to certain sized files.