12

Scenario: Am having Invoice__c Object. On the list view I have download button to download the selected invoices PDF's. What my below code is doing is it is after fetching the selected records id's of the PDF it is saving to notes and attachments and then downloading from there. Now I don't want to save to notes and attachments before downloading. I want to save in the program itself using some variables or anything. How can I do this.

My code is below

global class DownloadInvoice 
{
 // Receive Attachments info from Attachment ParentId
   private static String API_STATUS_NORMAL = '200';

   webService static string getAttachmentByParentId(string sfdcId)
    {

         List<id> ids = new List<id>();
         if(string.isempty(sfdcId))  {
            return DownloadInvoiceResponse.errorJson('Parameter sfdcId is required.');
        }
        system.debug('SFDCid'+sfdcId);
        string[] idsArray = sfdcId.split(',');
        for(integer i=0; i<idsArray.size();i++)
        {
           ids.add(idsArray[i]);
        }

        // Added this code to restrict batch download for some of the accounts who all are having uncheck for this field  Print_Invoice__c
        List<Invoice__c> invList = New List<Invoice__c>();
        invList = [Select id, name, Print_Invoice__c, Account__r.name from Invoice__c where ID IN: ids];
        List<Attachment> attInsert = new List<Attachment>();
        if(invList.size() > 0){
            System.debug(' ====> invList size <==== ');
            for(Invoice__c invc : invList){
                System.debug(' ====> Invoice is : <==== ' + invc);
                String msg = 'Selected invoices are not eligible for Invoice Printing, please check the Account '+invc.Account__r.name;
                if(invc.Print_Invoice__c == false){
                    System.debug(' ====> invc.Print_Invoice__c value false <==== ');
                    return DownloadInvoiceResponse.errorJson( msg );
                }

                PageReference pdf = new PageReference('/apex/InvoicePDF?id='+invc.id);
                //Attachment attach = new Attachment();
                Attachment attach = new Attachment();
                if (attach == null) attach = new Attachment();
                Blob body;
                try {
                    if(Test.IsRunningTest()){
                        System.debug(' ==> as Test Class <== ');
                        body = Blob.valueOf('Some Text');
                    }
                    else{
                        System.debug(' ==> as Apex normal Class <== ');
                        body = pdf.getContentAsPDF();   
                    }
                 } 
                catch (VisualforceException e) {
                    String msg2 = e.getMessage();
                    return DownloadInvoiceResponse.errorJson( msg2 );
                 }

          *******HERE AM SAVING TO ATTACHMENT********
          *******I DONT WANT TO SAVE TO NOTES AND ATTACHMENTS********

                attach.Body = body;
                attach.name= invc.name +'_Invoice_'+ Datetime.Now() +'.PDF';
                attach.IsPrivate = false;
                attach.ParentId = invc.id;
                attach.contentType = 'application/pdf';
                attInsert.add(attach);
            }            
        }

    ******Inserting to Attachments********

        Insert attInsert;

        integer totalSizeOfFiles=0;
        integer totalSizeAnInvoice=0;
        String invoiceId='';
        set<String> remainingsIdsSet=new set<String>();
        List<attachment> attachmentList = new List<attachment>();
          //for(attachment att:[select ParentId,id,Name,Body,contenttype from attachment where ParentId IN:ids]) {
          for(attachment att: attInsert) {          
                integer eachFileSize=att.Body.size();
                String parentId=att.ParentId;
                att.contenttype='application/pdf';
                if(!invoiceId.equals(parentId)){
                    invoiceId=parentId;
                    totalSizeAnInvoice=eachFileSize;
                    System.debug('--ID: '+att.id+'. ParentId: '+parentId+'. FileSize: '+eachFileSize+'. TotalInvoiceSize: '+totalSizeAnInvoice);
                }else if(invoiceId.equals(parentId)){
                    totalSizeAnInvoice=totalSizeAnInvoice+eachFileSize;
                    System.debug('--ID: '+att.id+'. ParentId: '+parentId+'. FileSize: '+eachFileSize+'. TotalInvoiceSize: '+totalSizeAnInvoice);
                }
                if(eachFileSize<4500000 && totalSizeAnInvoice<4500000){
                    totalSizeOfFiles=totalSizeOfFiles+eachFileSize;
                    System.debug('--ID: '+parentId+'. FileSize: '+eachFileSize+'. TotalFileSize: '+totalSizeOfFiles+'. HeapSize: '+Limits.getHeapSize());
                    if(totalSizeOfFiles>= 4500000){
                          System.debug('--Adding to RemIDs ID: '+parentId+'. FileSize: '+eachFileSize);
                          remainingsIdsSet.add(parentId);
                     }else{
                          attachmentList.add(att);                      
                     }
                 }
             }
             String remainingIds=null;
             List<String> remainingIdList=new List<String>(remainingsIdsSet);
             for(integer i=0;i<remainingIdList.size();i++){
                 if(i==0){
                     remainingIds=remainingIdList.get(i);
                 }else{
                     remainingIds=remainingIds+','+remainingIdList.get(i);
                  }                 
             }

             List<Object> dataList = new List<Object>();
             for(Attachment at :attachmentList)
             {
                Map<String, String> atMap = new Map<String, String>();
                atMap.put( 'Name', at.Name );
                atMap.put( 'Body', EncodingUtil.base64Encode( at.body ));
                datalist.add( atMap );

             }

                 Map<String, Object> response = new Map<String, Object>();
                 response.put('status', API_STATUS_NORMAL);
                 if( datalist != null ){
                     response.put('data',datalist);
                     response.put('id', remainingIds);
                 }
                 return json.serialize( response );


     }



         // Save Zip file to Document
   webService static String saveToDocument( String zipFileData, String fileName ){
       try{
            String userId = UserInfo.getUserId();
            List<Document> docList = [SELECT Id, Name, FolderId, Body FROM Document WHERE Name = :fileName AND FolderId = :userId];
            Document doc = new Document();
            if( docList == null || docList.size() == 0 ) {
                doc.Name = fileName;
                doc.FolderId = UserInfo.getUserId();
                doc.Body = EncodingUtil.base64Decode( zipFileData );
                System.debug(' Insert Doc @@@@ ' + doc);
                insert doc;
               } 
            else {
                doc = docList.get(0);
                doc.Body = EncodingUtil.base64Decode( zipFileData );
                update doc;
            }
            System.debug('--ZipFileName: '+fileName+'. DocId: '+doc.Id);
            return DownloadInvoiceResponse.normalJson( doc.Id );
        } catch ( Exception ex ) {
            return DownloadInvoiceResponse.errorJson( ex.getMessage() );
        }
    }

}

Onclick Javascript of Download Button on List view

    {!REQUIRESCRIPT("/resource/jqueryInvoice")} 
{!REQUIRESCRIPT("/resource/JSzipfileInvoice")} 
{!REQUIRESCRIPT("/soap/ajax/30.0/connection.js")} 
{!REQUIRESCRIPT("/soap/ajax/30.0/apex.js")} 

var records = {!GetRecordIds($ObjectType.Invoice__c)}; 
var newRecords = []; 

var SelectedIds=''; 
var fileDatafinal; 
for(var i=0;i < records.length; i++) 
{ 
SelectedIds+=records[i]+","; 
} 
SelectedIds=SelectedIds.substring(0,SelectedIds.length - 1); 
//alert(SelectedIds); 


if(SelectedIds.length > 0){ 

//while(SelectedIds!=null && SelectedIds.length > 0){ 
//alert('Selected Ids: ' + SelectedIds); 
alert(' Please wait until end of the process '); 
var response = sforce.apex.execute("DownloadInvoice","getAttachmentByParentId",{sfdcId:SelectedIds}); 

var respObj = JSON.parse(response); 
if( respObj['status'] != '200' ) 
{ 
alert( respObj['error'] ); 
} 
else 
{ 
var fileData = respObj['data']; 
SelectedIds=respObj['id']; 
//alert('RemainingIds Added to DownloadList: '+SelectedIds); 
downloadZip(fileData); 
} 
//} 
} 
else 
{ 
alert('Please select atleast one record'); 
} 


function downloadZip( fileData) { 

var fileName = 'Attachments.zip'; 
var zip = new JSZip(); 
for( var k in fileData ) { 
zip.file(fileData[k]['Name'], fileData[k]['Body'].toString(),{base64: true}); 
} 
content = zip.generate(); 
response = sforce.apex.execute("DownloadInvoice","saveToDocument",{zipFileData:content, fileName:fileName}); 

var fileObj = JSON.parse(response); 
if( fileObj['status'] != '200' ) { 
alert( fileObj['error'] ); 
return; 
} 
var docId = fileObj['data']; 
//alert('Starting download of: '+docId); 
alert('Please confirm to download Zip file'); 
/*setTimeout(function(){document.location.href = '/servlet/servlet.FileDownload?file=' + docId;},500);*/ 

window.location = '/servlet/servlet.FileDownload?file=' + docId; 

try { 

for (var n=0; n<records.length; n++){ 
//alert('==>1'); 
var sv = new sforce.SObject("Invoice__c"); 
//alert('==>2'); 
sv.id = records[n]; 
//alert('==>3'); 
sv.Is_Printed__c = true; 
//alert('==>4'); 
newRecords.push(sv); 
//alert('==>5'); 
} 

result = sforce.connection.update(newRecords); 
//alert('alert for update'+result); 
//window.location.reload(); 

} 

catch (e) { 

alert(e); 

} 

}

Image for Download button on the list view

Image showing file is Saving to notes and attachments

Image shows zip file download

thanks!

5
  • @Mohith Shrivastava updated question Commented Jan 4, 2016 at 7:48
  • Hey @mohammed-azarudeen looks like you have already completed functionality. you don't want to save the attachment. Then do one thing after pdf download call one more method get all the generated attachments based on select invoice Id and delete them..
    – Ratan Paul
    Commented Jan 6, 2016 at 6:07
  • @Ratan Yeah I have the idea too. If I have large number of selected id's then it will take more time to complete the process. Anyhow that is my last way if i cannot find a solution. Commented Jan 6, 2016 at 6:11
  • One of the ways may be using JS code to render pdf based on records. There are a lot of minuses --- like no flexibility from using standard VF page, and a lot of declarative JS. But pluses also here -- you can do more flexible layout like generate QR code dynamically etc Personally I have used jsPDF from github.com/MrRio/jsPDF
    – kurunve
    Commented Jan 6, 2016 at 13:53
  • @kurunve-jsPDF is actually will change the format/template of existing Invoice PDF. Any way with the apex is more useful. Commented Jan 7, 2016 at 5:27

5 Answers 5

6

You can do following to achieve this:

Add following javascript in your page

  1. FileSaver.min.js
  2. Blob.js
  3. jszip.min.js

  4. Pass All Id's to controller and just get there data by getContetnAsPdf one by one (Just create then Attachment object,don't insert them)

  5. Save them to a javascript array
  6. Convert them to zip file in javascript
  7. Download them using FileSaver.js

Let me know if you found it difficult to implement

Thanks

1
  • 2
    I didn't use Blob.js but it is working perfectly with filesaver and jszip. Thanks for this valuable steps. I also Posted my final result under Answer. Commented Jan 13, 2016 at 8:10
7
+50

Kudos to @SFDCGOD for suggesting this. I was so intrigued by his suggestion, I gave it a try.

First, I created resources for each of the Javascript files (FileSaver.js and jszip.js) and then created a simple Visualforce page referencing them so I could use it as a listview button:

<apex:page standardController="c2g__codaInvoice__c" recordSetVar="invoices" extensions="InvoiceDownload">
    <apex:includeScript value="{!$Resource.FileSaver}"/>
    <apex:includeScript value="{!$Resource.jszip}"/>

    <apex:form >
        <apex:pageBlock title="File Download">
            <apex:pageMessage severity="INFO" title="Invoice Download" strength="2" summary="Invoices Downloading" detail="Downloaded {!NumberOfFiles} file."/>
            <apex:pageBlockButtons >
                <apex:commandButton action="{!cancel}" value="Close"/>
            </apex:pageBlockButtons>
        </apex:pageBlock>
    </apex:form>    

    <script> 
    var download = function() {
        var files = JSON.parse('{!FilesToDownload}');
        var zip = new JSZip();
        for(fileName in files){
            var fileBase64String = files[fileName];
            zip.file(fileName, fileBase64String, { base64: true });                
        }
        saveAs(zip.generate({type:"blob"}), 'Invoices Download.zip', false);
    }
    download();
    </script></apex:page>

Then a I created a simple Controller to generate the Invoice(s) selected as PDFs converted to Base64 so that jszip can store PDFs:

public class InvoiceDownload 
{
    private List<c2g__codaInvoice__c> selected;
    public InvoiceDownload(ApexPages.StandardSetController ssc)
    {
        this.selected = [select name from c2g__codaInvoice__c where id in :ssc.getSelected()];
    }

    public Integer getNumberOfFiles()
    {
        return this.selected.size();
    }

    public String getFilesToDownload()
    {
        Map<String,String> files = new Map<String,String>();
        for(c2g__codaInvoice__c invoice : this.selected)
        {
            Blob pdf = new PageReference('/apex/SalesInvoiceCustomized?id='+invoice.Id).getContentAsPDF();
            files.put(invoice.Name+'.pdf', EncodingUtil.base64Encode(pdf));
        }
        return JSON.serialize(files);
    }
}

It works a treat, I only have 35 invoices in my DE org but no problems encountered with that number (PDFs are ~14k in size each). Beware though, this is Browser specific - I tested against Chrome, Firefox and Safari on Mac and it didn't work with Safari. Also tested on Chrome and IE on Windows 10 which also worked (I'm afraid they are all the browsers I have access to).

1
  • 2
    @Phil Hawthorn - Bravo!!. Thanks to your snippet I just followed to my scenario and it is working perfectly. I also Post my result under answer. Commented Jan 13, 2016 at 8:08
3

fantastic work done by @SFDCGOD and @Phil Hawthorn

Finally I got the result of downloading the zip files without saving to anywhere.

Special thanks to @Phil Hawthorn because I followed your methods. I customized for my Onclick javascript button and Werbservice methods instead of visualforce button.

Here is my final result. First I created Apex global class with Webservice method that returns the files

global class InvoiceDownload {

    //Private List<Invoice__c> selected;
    private static String API_STATUS_NORMAL = '200';

    webservice static String getFilesToDownload(string sfdcId)
    {
        List<id> ids = new List<id>();
        system.debug('SFDCid'+sfdcId);
        string[] idsArray = sfdcId.split(',');
        for(integer i=0; i<idsArray.size();i++)
        {
           ids.add(idsArray[i]);
        }
        List<Invoice__c> selected = [select name from Invoice__c where id in : ids];
        Map<String,String> files = new Map<String,String>();
        for(Invoice__c inv : selected)
        {
            Blob pdf = new PageReference('/apex/InvoicePDF?id='+inv.Id).getContentAsPDF();
            files.put(inv.Name+'.pdf', EncodingUtil.base64Encode(pdf));
        }
        return JSON.serialize(files);
    }
}

And I called the class and webservice method to the JSON.response method in an Onclick Javascript button.

{!REQUIRESCRIPT("/resource/JSzipfileInvoice")}
{!REQUIRESCRIPT("/resource/filesaver")} 
{!REQUIRESCRIPT("/soap/ajax/30.0/connection.js")} 
{!REQUIRESCRIPT("/soap/ajax/30.0/apex.js")} 

var download = function() {
        var records = {!GetRecordIds($ObjectType.Invoice__c)}; 
        var SelectedIds=''; 
        var fileDatafinal; 
        for(var i=0;i < records.length; i++) 
           { 
           SelectedIds+=records[i]+","; 
           } 
        SelectedIds=SelectedIds.substring(0,SelectedIds.length - 1);
        if(SelectedIds.length > 0){ 
           alert(' Please wait until end of the process '); 
           var response = sforce.apex.execute("InvoiceDownload","getFilesToDownload",{sfdcId:SelectedIds});
           var files = JSON.parse(response);        
           var zip = new JSZip();
           for(fileName in files){
              var fileBase64String = files[fileName];
              zip.file(fileName, fileBase64String, { base64: true });
            }
            saveAs(zip.generate({type:"blob"}), 'Invoice Download.zip', false);
            confirm('Please confirm to download');
        }
        else
        { 
          alert('Please select atlease one record');
        }
}
download();

Thanks to all of your responses.

0

You can return List of attachments in your getAttachmentByParentId method. It should look like this;

return JSON.serialize(yourattachmentlist);

It will return attachment objects in JSON form. Then you can send this to downloadZip function.

var response = sforce.apex.execute("DownloadInvoice","getAttachmentByParentId",{sfdcId:SelectedIds}); 
alert(response);
var fileObj = JSON.parse(response); 
alert(fileObj);
for(var i=0; i < fileObj.length;i++){
alert(fileObj[i].Id); -->> document ids
}
downloadZip(fileObj);
4
  • i don't want to return attachments. I just want to return my selected Invoices PDF's. Problem is it is saving to notes and attachments. I don't want to save to attachments. Commented Jan 6, 2016 at 5:43
  • are you returning attachmentlist after inserting attachments? Commented Jan 6, 2016 at 5:49
  • No, before insert.
    – tugce
    Commented Jan 6, 2016 at 8:48
  • it will be more helpful if you give full javascript with your code. Commented Jan 6, 2016 at 9:20
0

FYI: The generate() method has been replaced by generateAsync() and generateNodeStream()

Upgraded guidelines

You must log in to answer this question.

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