1
votes

In my WebApp, I generate bill with Prawn.

I'd like to download all bills in zipped file !

I did that :

def download
  bills = Bill.search(params[:q]).result(distinct: true).paginate(:page => params[:page], limit: 20, order: "paid_at DESC")

  bills.each do |b|
    Prawn::Document.generate("#{Rails.root}/public/pdfs/web_#{b.reference}.pdf")
  end


  require 'rubygems'
  require 'zip'
  zipfile_name = "#{Rails.root}/public/pdfs/factures.zip"
  Zip::File.open(zipfile_name, Zip::File::CREATE) do |zipfile|
    bills.each do |bill|
      zipfile.add("web_#{bill.reference}.pdf", "#{Rails.root}/public/pdfs/web_#{bill.reference}.pdf")
    end
  end

  path = File.join(Rails.root, "public", "pdfs")
  send_file File.join(path, "factures.zip")
end

But the PDFs are empty because I do not specify the "*.prawn" file that allow to generate the PDF !

Can anybody, give me the way to do that ?

Thanks

EDIT

My show.pdf.prawn

prawn_document() do |pdf|


pdf.image "#{Rails.root}/app/assets/images/logo.jpg"
  pdf.move_down 30

  pdf.move_down 10
  pdf.text "<b>Email :</b> <a href='mailto:[email protected]'>[email protected]</a>", inline_format: true
  pdf.text "<b>Site :</b> <a href='http://www.mtaville.fr'>www.mtaville.fr</a>", inline_format: true

  pdf.move_up 230
  pdf.table [["Facture N° #{@bill.reference}"],["Date : #{ldate(@bill.paid_at, format: :short2)}"]], position: :right do
    cells.style do |c|
      c.background_color = "2857BE"
      c.text_color = "FFFFFF"
      c.border_color = "2857BE"
      c.align = :center
    end
  end

  pdf.move_down 30
  pdf.table [["CLIENT"]], position: :right do
    cells.style do |c|
      c.background_color = "2857BE"
      c.text_color = "FFFFFF"
      c.border_color = "2857BE"
      c.width = 280
    end
  end

  pdf.bounding_box([260, 600], :width => 280, :height => 80) do
    pdf.text "#{@bill.billable.name}", style: :bold
    pdf.text "#{@bill.billable.address_1} #{@bill.billable.address_2}"
    pdf.text "#{@bill.billable.zipcode} - <b>#{@bill.billable.town}</b>", inline_format: true
  end

  pdf.move_down 100
  pdf.text "<b>Objet :</b> Facture n° #{@bill.reference}", inline_format: true

  if @bill.billable_type == 'Subscription'
    pdf.move_down 10
    data = [
      ["Produit", "Prix unitaire HT", "Prix total HT"],
      ["Renouvellement de souscription à votre espace privé", "#{number_to_currency(@bill.total)}", "#{number_to_currency @bill.total}" ]
    ]
    pdf.table data, width: 540 do
      cells.style do |c|
        c.background_color = (c.row == 0)? "2857BE" : "ffffff"
        c.text_color = (c.row == 0)? "ffffff" : "000000"
        c.font_style = (c.row == 0)? :bold : :normal
      end
    end
  end
  if @bill.billable_type == 'Order'
    pdf.move_down 10
    data = [
      ["Produit", "Prix unitaire HT", "Prix total HT"],
      ["Annonce n°#{@bill.billable.ad.id}", "#{number_to_currency(@bill.total)}", "#{number_to_currency @bill.total}" ]
    ]
    pdf.table data, width: 540 do
      cells.style do |c|
        c.background_color = (c.row == 0)? "2857BE" : "ffffff"
        c.text_color = (c.row == 0)? "ffffff" : "000000"
        c.font_style = (c.row == 0)? :bold : :normal
      end
    end
  end

  pdf.move_down 20
  data = [
    ["Total HT", "#{number_to_currency(@bill.total)}" ],
    ["Total TVA #{@bill.tva}%", "#{number_to_currency(@bill.amount_vat)}"],
    ["Total TTC", "#{number_to_currency(@bill.amount_inclusive_of_tax)}"]
  ]
  pdf.table data, position: :right do
    cells.style do |c|
      c.font_style = (c.row % 2 == 0)? :bold : :normal
      c.width = 140
      c.align = (c.column == 1)? :right : :left
    end
  end

  pdf.move_down 20
  data = [["NET A PAYER", "#{number_to_currency(0)}"]]
  pdf.table data, position: :right do
    cells.style do |c|
      c.font_style = :bold
      c.background_color = "2857BE"
      c.text_color = "ffffff"
      c.width = 140
      c.align = (c.column == 1)? :right : :left
    end
  end

end

** ANSWER **

Here is the fonction in my controller :

def download
    bills = Bill.search(params[:q]).result(distinct: true).paginate(:page => params[:page], limit: 20, order: "paid_at DESC")

    require 'rubygems'
    require 'zip'
    require 'bill'
    zipfile_name = "#{Rails.root}/tmp/pdfs/factures.zip"
    Zip::File.open(zipfile_name, Zip::File::CREATE) do |zipfile|
      bills.each do |bill|
        temp_pdf = Tempfile.new("web_#{bill.reference}.pdf")
        temp_pdf.binmode
        temp_prawn_pdf = BillPdf.new(bill)
        temp_pdf.write temp_prawn_pdf.render
        temp_pdf.rewind
        zipfile.add("web_#{bill.reference}.pdf", "#{temp_pdf.path}")
        temp_pdf.close
      end
    end

    path = File.join(Rails.root, "tmp", "pdfs")
    send_file File.join(path, "factures.zip")
  end

And my class :

class BillPdf < Prawn::Document
  include ActionView::Helpers::TranslationHelper
  include ActionView::Helpers::NumberHelper
  def initialize(bill)
    super()
    image "#{Rails.root}/app/assets/images/logo.jpg"

    move_down 10
    text "<b>N° TVA :</b> TVA", inline_format: true
    text "<b>Siret :</b> 789 618 691 00016", inline_format: true
    text "<b>Site :</b> <a href='http://www.mtaville.fr'>www.mtaville.fr</a>", inline_format: true

    move_up 230
    table [["Facture N° #{bill.reference}"],["Date : #{l(bill.paid_at, format: :short2)}"]], position: :right do
      cells.style do |c|
        c.background_color = "2857BE"
        c.text_color = "FFFFFF"
        c.border_color = "2857BE"
        c.align = :center
      end
    end

    move_down 30
    table [["CLIENT"]], position: :right do
      cells.style do |c|
        c.background_color = "2857BE"
        c.text_color = "FFFFFF"
        c.border_color = "2857BE"
        c.width = 280
      end
    end

    bounding_box([260, 600], :width => 280, :height => 80) do
      text "#{bill.billable.name}", style: :bold
      text "#{bill.billable.address_1} #{bill.billable.address_2}"
      text "#{bill.billable.zipcode} - <b>#{bill.billable.town}</b>", inline_format: true
    end

    move_down 100
    text "<b>Objet :</b> Facture n° #{bill.reference}", inline_format: true

    if bill.billable_type == 'Subscription'
      move_down 10
      data = [
        ["Produit", "Prix unitaire HT", "Prix total HT"],
        ["Renouvellement de souscription à votre espace privé", "#{number_to_currency(bill.total)}", "#{number_to_currency bill.total}" ]
      ]
      table data, width: 540 do
        cells.style do |c|
          c.background_color = (c.row == 0)? "2857BE" : "ffffff"
          c.text_color = (c.row == 0)? "ffffff" : "000000"
          c.font_style = (c.row == 0)? :bold : :normal
        end
      end
    end
    if bill.billable_type == 'Order'
      move_down 10
      data = [
        ["Produit", "Prix unitaire HT", "Prix total HT"],
        ["Annonce n°#{bill.billable.ad.id}", "#{number_to_currency(bill.total)}", "#{number_to_currency bill.total}" ]
      ]
      table data, width: 540 do
        cells.style do |c|
          c.background_color = (c.row == 0)? "2857BE" : "ffffff"
          c.text_color = (c.row == 0)? "ffffff" : "000000"
          c.font_style = (c.row == 0)? :bold : :normal
        end
      end
    end

    move_down 20
    data = [
      ["Total HT", "#{number_to_currency(bill.total)}" ],
      ["Total TVA #{bill.tva}%", "#{number_to_currency(bill.amount_vat)}"],
      ["Total TTC", "#{number_to_currency(bill.amount_inclusive_of_tax)}"]
    ]
    table data, position: :right do
      cells.style do |c|
        c.font_style = (c.row % 2 == 0)? :bold : :normal
        c.width = 140
        c.align = (c.column == 1)? :right : :left
      end
    end

    move_down 20
    data = [["NET A PAYER", "#{number_to_currency(0)}"]]
    table data, position: :right do
      cells.style do |c|
        c.font_style = :bold
        c.background_color = "2857BE"
        c.text_color = "ffffff"
        c.width = 140
        c.align = (c.column == 1)? :right : :left
      end
    end
  end

end
1
You could use Tempfile to generate a temporary file and add it to the zip. Make sure that you close the file once you're done using it. - kobaltz
Ok, but How can I add content in that tempfile ? - p0k3
See my Answer, I've expanded on this thought. - kobaltz

1 Answers

4
votes

To expand on my comment more, see the code refactor below. Instead of adding the files to your public directory, which is dangerous for security reasons, you can use Tempfiles. You may need to tweak this a bit, but should give you the basic idea behind Tempfiles.

def download
  bills = Bill.search(params[:q]).result(distinct: true).paginate(:page => params[:page], limit: 20, order: "paid_at DESC")

  require 'rubygems'
  require 'zip'
  zipfile_name = Tempfile.new(["#{Rails.root}/tmp/factures", '.zip'])
  Zip::File.open(zipfile_name, Zip::File::CREATE) do |zipfile|
    bills.each do |bill|
      temp_pdf = Tempfile.new(["basic_questions_#{Time.now.to_i}", '.pdf'])
      temp_pdf.binmode
      temp_prawn_pdf = Prawn::Document.new(#WHATEVER YOUR PARAMETERS ARE)
      temp_pdf.write temp_prawn_pdf.render
      temp_pdf.rewind
      zipfile.add("web_#{bill.reference}.pdf", "#{temp_pdf.path}")
      temp_pdf.close
    end
  end

  path = File.join(Rails.root, "public", "pdfs")
  send_file File.join(path, "factures.zip")
ensure
  zipfile_name.close
end