Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Got rid of Gemfile.lock.

  • Loading branch information...
commit f65b2aac824fff866113bff9c097908d66ee05b6 1 parent a83816a
@commondream authored
View
6 .gitignore
@@ -1,3 +1,7 @@
tmp/
doc/
-.yardoc/
+.yardoc/
+*.gem
+Gemfile.lock
+.bundle
+pkg/*
View
2  Gemfile
@@ -1,5 +1,7 @@
source "http://rubygems.org"
+gemspec
+
gem "prawn", "0.11.1.pre"
group :development do
View
18 Gemfile.lock
@@ -1,18 +0,0 @@
-GEM
- remote: http://rubygems.org/
- specs:
- Ascii85 (1.0.0)
- pdf-reader (0.9.1)
- Ascii85 (>= 0.9)
- prawn (0.11.1.pre)
- pdf-reader (>= 0.8.1)
- rake (0.8.7)
- yard (0.6.4)
-
-PLATFORMS
- ruby
-
-DEPENDENCIES
- prawn (= 0.11.1.pre)
- rake
- yard
View
71 README.md
@@ -1,4 +1,73 @@
Payday!
===
+Payday is a library for rendering invoices. At present it supports rendering invoices to pdfs, but we're planning on adding support for other formats in the near future.
-Payday is a library for rendering invoices. At present it supports rendering invoices to html and pdfs, but maybe we'll support other formats in the future it's needed.
+Using Payday
+===
+It's pretty easy to use Payday with the built in objects. We include the Invoice and LineItem classes, and with them you can get started pretty quickly.
+
+Example:
+
+ invoice = Payday::Invoice.new(:invoice_number => 12)
+ i.line_items << LineItem.new(:price => 20, :quantity => 5, :description => "Pants")
+ i.line_items << LineItem.new(:price => 10, :quantity => 3, :description => "Shirts")
+ i.line_items << LineItem.new(:price => 5, :quantity => 200, :description => "Hats")
+ i.render_pdf_to_file("/path/to_file.pdf")
+
+Customizing Your Logo and Company Name
+===
+Check out Payday::Config to customize your company's name, details, and logo.
+
+Example:
+
+ Payday::Config.default.invoice_log = "/path/to/company/logo.png"
+ Payday::Config.default.company_name = "Awesome Corp"
+ Payday::Config.default.company_details = "10 This Way\nManhattan, NY 10001\n800-111-2222\nawesome@awesomecorp.com"
+
+Using Payday with ActiveRecord Objects (or any other objects, for that matter)
+===
+
+TODO
+
+Rendering Payday PDFs To The Web
+===
+Payday's Invoiceable module includes methods for rendering pdfs to disk and for rendering them to a string. In a Rails controller, you can use the
+render to string method to render a pdf directly to the browser like this:
+
+In environment.rb:
+
+ Mime::Type.register 'application/pdf', :pdf
+
+In your controller:
+
+ respond_to do |format|
+ format.html
+ format.pdf do
+ send_data invoice.render_pdf, :filename => "Invoice #12.pdf", :type => "application/pdf", :disposition => :inline
+ end
+ end
+
+Contributing
+===
+Payday is pretty young, so there's still a good bit of work to be done. Feel free to fork the project, make some changes, and send a pull request. If you're unsure about what to work on, send me a message on GitHub. I'd love the help!
+
+To Do
+===
+Here's what we're planning on working on with Payday in the near future:
+
+* Package as gem
+* Document how to use with ActiveRecord
+* Release 1.0!
+
+* Add support for currencies other than USD
+* Add support for Money gem or BigDecimal or general numerics (right now we support BigDecimal and general numerics)
+* Add support for blank line items
+* Add support for indented line items
+* Apply different tax rates to different line items
+* Add support for shipping either pre or post tax
+* Add ability to set pdf document size to something other than 8.5 x 11
+* Add invoice_details has for stuff under the invoice number
+* Add ability to show skus or product ids on each line item
+
+* Add page numbers
+* Ability to render invoice to html for web viewing
View
4 Rakefile
@@ -1,10 +1,12 @@
-require "bundler/setup"
+require 'bundler'
require 'rake'
require 'rake/testtask'
task :default => :test
+Bundler::GemHelper.install_tasks
+
Rake::TestTask.new do |t|
t.test_files = FileList['test/**/*_test.rb']
end
View
9 lib/payday/invoiceable.rb
@@ -39,8 +39,13 @@ def paid?
defined?(:paid_on) && !!paid_on
end
- # Renders this invoice to pdf
- def render_to_pdf
+ # Renders this invoice to pdf as a string
+ def render_pdf
Payday::PdfRenderer.render(self)
end
+
+ # Renders this invoice to pdf
+ def render_pdf_to_file(path)
+ Payday::PdfRenderer.render_to_file(self, path)
+ end
end
View
268 lib/payday/pdf_renderer.rb
@@ -4,145 +4,153 @@ module Payday
# {{Payday::Invoiceable#render_pdf}} to render pdfs yourself.
class PdfRenderer
- # Renders the given invoice as a pdf.
+ # Renders the given invoice as a pdf on disk
+ def self.render_to_file(invoice, path)
+ pdf(invoice).render_file(path)
+ end
+
+ # Renders the given invoice as a pdf, returning a string
def self.render(invoice)
- pdf = Prawn::Document.new
-
- # set up some default styling
- pdf.font_size(8)
-
- # render the logo
- logo_info = pdf.image(invoice_or_default(invoice, :invoice_logo), :at => pdf.bounds.top_left, :fit => [200, 100])
-
- # render the stamp
- stamp = nil
- if invoice.paid?
- stamp = "PAID"
- elsif invoice.overdue?
- stamp = "OVERDUE"
- end
+ pdf(invoice).render
+ end
+
+ private
+ def self.pdf(invoice)
+ pdf = Prawn::Document.new
+
+ # set up some default styling
+ pdf.font_size(8)
+
+ # render the logo
+ logo_info = pdf.image(invoice_or_default(invoice, :invoice_logo), :at => pdf.bounds.top_left, :fit => [200, 100])
- pdf.bounding_box([200, pdf.cursor - 50], :width => pdf.bounds.width - 400) do
- pdf.font("Helvetica-Bold") do
- pdf.fill_color "cc0000"
- pdf.text stamp, :align=> :center, :size => 25, :rotate => 15
+ # render the stamp
+ stamp = nil
+ if invoice.paid?
+ stamp = "PAID"
+ elsif invoice.overdue?
+ stamp = "OVERDUE"
end
- end
- pdf.fill_color "000000"
-
- # render the company details
- table_data = []
- table_data << [bold_cell(pdf, invoice_or_default(invoice, :company_name), :size => 12)]
- table_data << [invoice_or_default(invoice, :company_details)]
- table = pdf.make_table(table_data, :cell_style => { :borders => [], :padding => [2, 0] })
- pdf.bounding_box([pdf.bounds.width - table.width, pdf.bounds.top], :width => table.width, :height => table.height) do
- table.draw
- end
-
- bill_to_cell_style = { :borders => [], :padding => [2, 0]}
-
- # render bill to
- pdf.move_cursor_to(pdf.bounds.top - logo_info.scaled_height - 20)
- pdf.float do
- pdf.table([[bold_cell(pdf, "Bill To")], [invoice.bill_to]], :column_widths => [200], :cell_style => bill_to_cell_style)
- end
-
- # render ship to
- table = pdf.make_table([[bold_cell(pdf, "Ship To")], [invoice.ship_to]], :column_widths => [200],
- :cell_style => bill_to_cell_style)
- pdf.bounding_box([pdf.bounds.width - table.width, pdf.cursor], :width => table.width, :height => table.height + 2) do
- table.draw
- end
-
- # render the invoice details
- pdf.move_cursor_to(pdf.cursor - 20)
- table_data = []
-
- # invoice number
- if defined?(invoice.invoice_number) && invoice.invoice_number
- table_data << [bold_cell(pdf, "Invoice #:"), bold_cell(pdf, invoice.invoice_number.to_s, :align => :right)]
- end
-
- # Due on
- if defined?(invoice.due_on) && invoice.due_on
- if invoice.due_on.is_a?(Date)
- due_date = invoice.due_on.strftime(Payday::Config.default.date_format)
- else
- due_date = invoice.due_on.to_s
+ pdf.bounding_box([200, pdf.cursor - 50], :width => pdf.bounds.width - 400) do
+ pdf.font("Helvetica-Bold") do
+ pdf.fill_color "cc0000"
+ pdf.text stamp, :align=> :center, :size => 25, :rotate => 15
+ end
end
-
- table_data << [bold_cell(pdf, "Due Date:"), bold_cell(pdf, due_date, :align => :right)]
- end
-
- # Paid on
- if defined?(invoice.paid_on) && invoice.paid_on
- if invoice.paid_on.is_a?(Date)
- paid_date = invoice.paid_on.strftime(Payday::Config.default.date_format)
- else
- paid_date = invoice.paid_on.to_s
+
+ pdf.fill_color "000000"
+
+ # render the company details
+ table_data = []
+ table_data << [bold_cell(pdf, invoice_or_default(invoice, :company_name), :size => 12)]
+ table_data << [invoice_or_default(invoice, :company_details)]
+ table = pdf.make_table(table_data, :cell_style => { :borders => [], :padding => [2, 0] })
+ pdf.bounding_box([pdf.bounds.width - table.width, pdf.bounds.top], :width => table.width, :height => table.height) do
+ table.draw
end
-
- table_data << [bold_cell(pdf, "Paid Date:"), bold_cell(pdf, paid_date, :align => :right)]
- end
-
- if table_data.length > 0
- pdf.table(table_data, :cell_style => { :borders => [], :padding => 1 })
- end
-
- # render the line items
- table_data = []
- table_data << [bold_cell(pdf, "Description", :borders => []),
- bold_cell(pdf, "Unit Price", :align => :center, :borders => []),
- bold_cell(pdf, "Quantity", :align => :center, :borders => []),
- bold_cell(pdf, "Amount", :align => :center, :borders => [])]
- invoice.line_items.each do |line|
- table_data << [line.description, number_to_currency(line.price), line.quantity.to_s("F"),
- number_to_currency(line.amount)]
- end
-
- pdf.move_cursor_to(pdf.cursor - 20)
- pdf.table(table_data, :width => pdf.bounds.width, :header => true, :cell_style => {:border_width => 0.5, :border_color => "cccccc", :padding => [5, 10]},
- :row_colors => ["dfdfdf", "ffffff"]) do
- # left align the number columns
- columns(1..3).rows(1..row_length - 1).style(:align => :right)
-
- # set the column widths correctly
- natural = natural_column_widths
- natural[0] = width - natural[1] - natural[2] - natural[3]
-
- column_widths = natural
- end
-
- # render the totals lines
- table_data = []
- table_data << [bold_cell(pdf, "Subtotal:"), cell(pdf, number_to_currency(invoice.subtotal), :align => :right)]
- table_data << [bold_cell(pdf, "Tax:"), cell(pdf, number_to_currency(invoice.tax), :align => :right)]
- table_data << [bold_cell(pdf, "Total:", :size => 12),
- cell(pdf, number_to_currency(invoice.total), :size => 12, :align => :right)]
- table = pdf.make_table(table_data, :cell_style => { :borders => [] })
- pdf.bounding_box([pdf.bounds.width - table.width, pdf.cursor], :width => table.width, :height => table.height + 2) do
- table.draw
- end
-
- # render the notes
- if defined?(invoice.notes) && invoice.notes
- pdf.move_cursor_to(pdf.cursor - 30)
- pdf.font("Helvetica-Bold") do
- pdf.text("Notes")
+
+ bill_to_cell_style = { :borders => [], :padding => [2, 0]}
+
+ # render bill to
+ pdf.move_cursor_to(pdf.bounds.top - logo_info.scaled_height - 20)
+ pdf.float do
+ pdf.table([[bold_cell(pdf, "Bill To")], [invoice.bill_to]], :column_widths => [200], :cell_style => bill_to_cell_style)
+ end
+
+ # render ship to
+ table = pdf.make_table([[bold_cell(pdf, "Ship To")], [invoice.ship_to]], :column_widths => [200],
+ :cell_style => bill_to_cell_style)
+ pdf.bounding_box([pdf.bounds.width - table.width, pdf.cursor], :width => table.width, :height => table.height + 2) do
+ table.draw
end
- pdf.line_width = 0.5
- pdf.stroke_color = "cccccc"
- pdf.stroke_line([0, pdf.cursor - 3, pdf.bounds.width, pdf.cursor - 3])
- pdf.move_cursor_to(pdf.cursor - 10)
- pdf.text(invoice.notes.to_s)
+
+ # render the invoice details
+ pdf.move_cursor_to(pdf.cursor - 20)
+ table_data = []
+
+ # invoice number
+ if defined?(invoice.invoice_number) && invoice.invoice_number
+ table_data << [bold_cell(pdf, "Invoice #:"), bold_cell(pdf, invoice.invoice_number.to_s, :align => :right)]
+ end
+
+ # Due on
+ if defined?(invoice.due_on) && invoice.due_on
+ if invoice.due_on.is_a?(Date)
+ due_date = invoice.due_on.strftime(Payday::Config.default.date_format)
+ else
+ due_date = invoice.due_on.to_s
+ end
+
+ table_data << [bold_cell(pdf, "Due Date:"), bold_cell(pdf, due_date, :align => :right)]
+ end
+
+ # Paid on
+ if defined?(invoice.paid_on) && invoice.paid_on
+ if invoice.paid_on.is_a?(Date)
+ paid_date = invoice.paid_on.strftime(Payday::Config.default.date_format)
+ else
+ paid_date = invoice.paid_on.to_s
+ end
+
+ table_data << [bold_cell(pdf, "Paid Date:"), bold_cell(pdf, paid_date, :align => :right)]
+ end
+
+ if table_data.length > 0
+ pdf.table(table_data, :cell_style => { :borders => [], :padding => 1 })
+ end
+
+ # render the line items
+ table_data = []
+ table_data << [bold_cell(pdf, "Description", :borders => []),
+ bold_cell(pdf, "Unit Price", :align => :center, :borders => []),
+ bold_cell(pdf, "Quantity", :align => :center, :borders => []),
+ bold_cell(pdf, "Amount", :align => :center, :borders => [])]
+ invoice.line_items.each do |line|
+ table_data << [line.description, number_to_currency(line.price), line.quantity.to_s("F"),
+ number_to_currency(line.amount)]
+ end
+
+ pdf.move_cursor_to(pdf.cursor - 20)
+ pdf.table(table_data, :width => pdf.bounds.width, :header => true, :cell_style => {:border_width => 0.5, :border_color => "cccccc", :padding => [5, 10]},
+ :row_colors => ["dfdfdf", "ffffff"]) do
+ # left align the number columns
+ columns(1..3).rows(1..row_length - 1).style(:align => :right)
+
+ # set the column widths correctly
+ natural = natural_column_widths
+ natural[0] = width - natural[1] - natural[2] - natural[3]
+
+ column_widths = natural
+ end
+
+ # render the totals lines
+ table_data = []
+ table_data << [bold_cell(pdf, "Subtotal:"), cell(pdf, number_to_currency(invoice.subtotal), :align => :right)]
+ table_data << [bold_cell(pdf, "Tax:"), cell(pdf, number_to_currency(invoice.tax), :align => :right)]
+ table_data << [bold_cell(pdf, "Total:", :size => 12),
+ cell(pdf, number_to_currency(invoice.total), :size => 12, :align => :right)]
+ table = pdf.make_table(table_data, :cell_style => { :borders => [] })
+ pdf.bounding_box([pdf.bounds.width - table.width, pdf.cursor], :width => table.width, :height => table.height + 2) do
+ table.draw
+ end
+
+ # render the notes
+ if defined?(invoice.notes) && invoice.notes
+ pdf.move_cursor_to(pdf.cursor - 30)
+ pdf.font("Helvetica-Bold") do
+ pdf.text("Notes")
+ end
+ pdf.line_width = 0.5
+ pdf.stroke_color = "cccccc"
+ pdf.stroke_line([0, pdf.cursor - 3, pdf.bounds.width, pdf.cursor - 3])
+ pdf.move_cursor_to(pdf.cursor - 10)
+ pdf.text(invoice.notes.to_s)
+ end
+
+ pdf
end
- # just dump it all out to a file for now
- pdf.render_file("tmp/testing.pdf")
- end
-
- private
def self.invoice_or_default(invoice, property)
if invoice.respond_to?(property) && invoice.send(property)
invoice.send(property)
View
22 test/invoice_test.rb
@@ -81,7 +81,10 @@ class InvoiceTest < Test::Unit::TestCase
assert i.paid?
end
- test "rendering to pdf" do
+ test "rendering to file" do
+ File.unlink("tmp/testing.pdf") if File.exists?("tmp/testing.pdf")
+ assert !File.exists?("tmp/testing.pdf")
+
i = Invoice.new(:tax_rate => 0.1, :notes => "These are some crazy awesome notes!", :invoice_number => 12,
:due_on => Date.civil(2011, 1, 22), :paid_on => Date.civil(2012, 2, 22),
:bill_to => "Alan Johnson\n101 This Way\nSomewhere, SC 22222", :ship_to => "Frank Johnson\n101 That Way\nOther, SC 22229")
@@ -92,7 +95,22 @@ class InvoiceTest < Test::Unit::TestCase
i.line_items << LineItem.new(:price => 5, :quantity => 200, :description => "Hats")
end
- i.render_to_pdf
+ i.render_pdf_to_file("tmp/testing.pdf")
+ assert File.exists?("tmp/testing.pdf")
+ end
+
+ test "rendering to string" do
+ i = Invoice.new(:tax_rate => 0.1, :notes => "These are some crazy awesome notes!", :invoice_number => 12,
+ :due_on => Date.civil(2011, 1, 22), :paid_on => Date.civil(2012, 2, 22),
+ :bill_to => "Alan Johnson\n101 This Way\nSomewhere, SC 22222", :ship_to => "Frank Johnson\n101 That Way\nOther, SC 22229")
+
+ 3.times do
+ i.line_items << LineItem.new(:price => 20, :quantity => 5, :description => "Pants")
+ i.line_items << LineItem.new(:price => 10, :quantity => 3, :description => "Shirts")
+ i.line_items << LineItem.new(:price => 5, :quantity => 200, :description => "Hats")
+ end
+
+ assert_not_nil i.render_pdf
end
end
end
Please sign in to comment.
Something went wrong with that request. Please try again.