Skip to content

Commit

Permalink
Use prawn-rails instead of custom View Handler, refactor invoice
Browse files Browse the repository at this point in the history
* Use prawn-rails
* Delete old view handler code
* Refactor invoice to partials
* Adjust table width so they're dynamic (and the template can also handle A4
* Refactor packaging slip, too
* Override prawn_rails' configuration
  • Loading branch information
mamhoff authored and futhr committed Jul 19, 2015
1 parent 8b8a9d3 commit a46c611
Show file tree
Hide file tree
Showing 18 changed files with 347 additions and 310 deletions.
106 changes: 106 additions & 0 deletions app/models/spree/invoice.rb
@@ -0,0 +1,106 @@
class Spree::Invoice < ActiveRecord::Base
belongs_to :invoiceable, polymorphic: true
validates :invoiceable, presence: true

before_save :set_date
before_save :set_number, if: :use_sequential_number?
after_save :increase_invoice_number, if: :use_sequential_number?

# Returns the given template as pdf binary suitable for Rails send_data
#
# If the file is already present it returns this
# else it generates a new file, stores and returns this.
#
# You can disable the pdf file generation with setting
#
# Spree::PrintInvoice::Config.store_pdf to false
#
def pdf_file(template)
if Spree::PrintInvoice::Config.store_pdf
send_or_create_pdf(template)
else
render_pdf(template)
end
end

# = The PDF filename
#
# Tries to take invoice_number attribute.
# If this is not present it takes the order number.
#
def pdf_filename
number.present? ? number : invoiceable.number
end

# = PDF file path for template name
#
def pdf_file_path(template)
Rails.root.join(pdf_storage_path(template), "#{pdf_filename}.pdf")
end

# = PDF storage folder path for given template name
#
# Configure the storage path with +Spree::PrintInvoice::Config.storage_path+
#
# Each template type gets it own pluralized folder inside
# of +Spree::PrintInvoice::Config.storage_path+
#
# == Example:
#
# pdf_storage_path('invoice') => "tmp/pdf_prints/invoices"
#
# Creates the folder if it's not present yet.
#
def pdf_storage_path(template)
storage_path = Rails.root.join(Spree::PrintInvoice::Config.storage_path, template.pluralize)
FileUtils.mkdir_p(storage_path)
storage_path
end

# Renders the prawn template for give template name in context of ActionView.
#
# Prawn templates need to be placed in +app/views/spree/admin/invoices/+ folder.
#
# Assigns +@invoice+ instance variable
#
def render_pdf(template)
ActionView::Base.new(
ActionController::Base.view_paths,
{invoice: self, template: template}
).render(template: "spree/admin/invoices/#{template}.pdf.prawn")
end

private

# Sends stored pdf for given template from disk.
#
# Renders and stores it if it's not yet present.
#
def send_or_create_pdf(template)
file_path = pdf_file_path(template)

unless File.exist?(file_path)
File.open(file_path, 'wb') { |f| f.puts render_pdf(template) }
end

IO.binread(file_path)
end

### AR Callback actions

def set_date
self.date ||= Date.today
end

def set_number
self.number ||= Spree::PrintInvoice::Config.next_number
end

def increase_invoice_number
Spree::PrintInvoice::Config.increase_invoice_number!
end

def use_sequential_number?
@_use_sequential_number ||= Spree::PrintInvoice::Config.use_sequential_number?
end
end
2 changes: 1 addition & 1 deletion app/models/spree/order_decorator.rb
Expand Up @@ -95,7 +95,7 @@ def send_or_create_pdf(template)
file_path = pdf_file_path(template)

unless File.exist?(file_path)
File.open(file_path, "wb") { |f| f.puts render_pdf(template) }
File.open(file_path, 'wb') { |f| f.puts render_pdf(template) }
end

IO.binread(file_path)
Expand Down
25 changes: 25 additions & 0 deletions app/views/spree/admin/invoices/_address_block.pdf.prawn
@@ -0,0 +1,25 @@
bill_address = invoice.invoiceable.bill_address
ship_address = invoice.invoiceable.ship_address

pdf.move_down 2
address_cell_billing = pdf.make_cell(content: Spree.t(:billing_address), font_style: :bold)
address_cell_shipping = pdf.make_cell(content: Spree.t(:shipping_address), font_style: :bold)

billing = "#{bill_address.firstname} #{bill_address.lastname}"
billing << "\n#{bill_address.address1}"
billing << "\n#{bill_address.address2}" unless bill_address.address2.blank?
billing << "\n#{bill_address.city}, #{bill_address.state_text} #{bill_address.zipcode}"
billing << "\n#{bill_address.country.name}"
billing << "\n#{bill_address.phone}"

shipping = "#{ship_address.firstname} #{ship_address.lastname}"
shipping << "\n#{ship_address.address1}"
shipping << "\n#{ship_address.address2}" unless ship_address.address2.blank?
shipping << "\n#{ship_address.city}, #{ship_address.state_text} #{ship_address.zipcode}"
shipping << "\n#{ship_address.country.name}"
shipping << "\n#{ship_address.phone}"
shipping << "\n\n#{Spree.t(:via, scope: :print_invoice)} #{@invoice.invoiceable.shipments.first.shipping_method.name}"

data = [[address_cell_billing, address_cell_shipping], [billing, shipping]]

pdf.table(data, position: :center, column_widths: [pdf.bounds.width / 2, pdf.bounds.width / 2])
14 changes: 14 additions & 0 deletions app/views/spree/admin/invoices/_footer.pdf.prawn
@@ -0,0 +1,14 @@
pdf.repeat(:all) do
pdf.grid([7,0], [7,4]).bounding_box do

data = []
data << [pdf.make_cell(content: Spree.t(:vat, scope: :print_invoice), colspan: 2, align: :center)]
data << [pdf.make_cell(content: '', colspan: 2)]
data << [pdf.make_cell(content: Spree::PrintInvoice::Config[:footer_left], align: :left),
pdf.make_cell(content: Spree::PrintInvoice::Config[:footer_right], align: :right)]

pdf.table(data, position: :center, column_widths: [pdf.bounds.width / 2, pdf.bounds.width / 2]) do
row(0..2).style borders: []
end
end
end
22 changes: 22 additions & 0 deletions app/views/spree/admin/invoices/_header.pdf.prawn
@@ -0,0 +1,22 @@
im = Rails.application.assets.find_asset(Spree::PrintInvoice::Config[:logo_path])

if im && File.exist?(im.pathname)
pdf.image im, vposition: :top, height: 40, scale: Spree::PrintInvoice::Config[:logo_scale]
end

pdf.grid([0,3], [1,4]).bounding_box do
pdf.text Spree.t(template.to_sym, scope: :print_invoice), align: :right, style: :bold, size: 18
pdf.move_down 4

pdf.text I18n.l(invoice.invoiceable.completed_at.to_date), align: :right if invoice.invoiceable.completed_at?

if Spree::PrintInvoice::Config.use_sequential_number? && invoice.number.present?
pdf.text Spree.t(:invoice_number, number: invoice.number), align: :right
pdf.move_down 2
pdf.text "#{Spree.t(:invoice_date, scope: :print_invoice)} #{I18n.l invoice.date}", align: :right
else
pdf.text Spree.t(:order_number, number: invoice.invoiceable.number), align: :right
pdf.move_down 2
pdf.text I18n.l(invoice.invoiceable.completed_at.to_date), align: :right
end
end
29 changes: 29 additions & 0 deletions app/views/spree/admin/invoices/_invoice_items.pdf.prawn
@@ -0,0 +1,29 @@
header = [
pdf.make_cell(content: Spree.t(:sku)),
pdf.make_cell(content: Spree.t(:item_description)),
pdf.make_cell(content: Spree.t(:options)),
pdf.make_cell(content: Spree.t(:price)),
pdf.make_cell(content: Spree.t(:qty)),
pdf.make_cell(content: Spree.t(:total))
]
data = [header]

invoice.invoiceable.line_items.each do |item|
row = [
item.variant.sku,
item.variant.name,
item.variant.options_text,
item.single_display_amount.to_s,
item.quantity,
item.display_total.to_s
]
data += [row]
end

column_widths = [0.13, 0.37, 0.185, 0.12, 0.075, 0.12].map { |w| w * pdf.bounds.width }

pdf.table(data, header: true, position: :center, column_widths: column_widths) do
row(0).style align: :center, font_style: :bold
column(0..2).style align: :left
column(3..6).style align: :right
end
@@ -0,0 +1 @@
_packaging_slip_items
11 changes: 11 additions & 0 deletions app/views/spree/admin/invoices/_page_number.pdf.prawn
@@ -0,0 +1,11 @@
string = "#{Spree.t(:page, scope: :print_invoice)} <page> #{Spree.t(:of, scope: :print_invoice)} <total>"

options = {
at: [pdf.bounds.right - 155, 0],
width: 150,
align: :right,
start_count_at: 1,
color: '000000'
}

pdf.number_pages string, options
40 changes: 40 additions & 0 deletions app/views/spree/admin/invoices/_totals.pdf.prawn
@@ -0,0 +1,40 @@
# TOTALS
totals = []

# Subtotal
totals << [pdf.make_cell(content: Spree.t(:subtotal)), invoice.invoiceable.display_item_total.to_s]

# Adjustments
invoice.invoiceable.all_adjustments.eligible.each do |adjustment|
totals << [pdf.make_cell(content: adjustment.label), adjustment.display_amount.to_s]
end

# Shipments
invoice.invoiceable.shipments.each do |shipment|
totals << [pdf.make_cell(content: shipment.shipping_method.name), shipment.display_cost.to_s]
end

# Totals
totals << [pdf.make_cell(content: Spree.t(:order_total)), invoice.invoiceable.display_total.to_s]

# Payments
total_payments = 0.0
invoice.invoiceable.payments.each do |payment|
totals << [
pdf.make_cell(
content: Spree.t(:payment_via,
gateway: (payment.source_type || Spree.t(:unprocessed, scope: :print_invoice)),
number: payment.number,
date: I18n.l(payment.updated_at.to_date, format: :long),
scope: :print_invoice)
),
payment.display_amount.to_s
]
total_payments += payment.amount
end

totals_table_width = [0.875, 0.125].map { |w| w * pdf.bounds.width }
pdf.table(totals, column_widths: totals_table_width) do
row(0..6).style align: :right
column(0).style borders: [], font_style: :bold
end

0 comments on commit a46c611

Please sign in to comment.