invoicepdf 0.1.4
Sign up to get free protection for your applications and to get access to all the features.
- data/LICENSE.txt +20 -0
- data/README.rdoc +63 -0
- data/Rakefile +53 -0
- data/init.rb +1 -0
- data/lib/generators/standard.rb +127 -0
- data/lib/invoice/helpers.rb +34 -0
- data/lib/invoice/invoice.rb +82 -0
- data/lib/invoice/line_item.rb +38 -0
- data/lib/invoicepdf.rb +12 -0
- data/test/helper.rb +18 -0
- data/test/test_invoice.rb +37 -0
- metadata +171 -0
data/LICENSE.txt
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2010 Drew Tempelmeyer
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.rdoc
ADDED
@@ -0,0 +1,63 @@
|
|
1
|
+
= InvoicePDF
|
2
|
+
|
3
|
+
InvoicePDF is a Ruby gem to easily create PDF invoices within your application.
|
4
|
+
|
5
|
+
I created this to be easily extensible by creating and specifying your own generators to create
|
6
|
+
invoices fit for your use.
|
7
|
+
|
8
|
+
== Getting Started
|
9
|
+
1. Install InvoicePDF as you would any other gem
|
10
|
+
gem install invoicepdf
|
11
|
+
|
12
|
+
2. Include the gem in your Gemfile for Rails applications
|
13
|
+
gem "invoicepdf"
|
14
|
+
|
15
|
+
3. Start using it in your application
|
16
|
+
invoice = InvoicePDF::Invoice.new( :company => "Drew Tempelmeyer", :company_address => "555 55th St\nNew York, NY 00000", :bill_to => "John Doe", :number => "AZ-100", :notes => "Test invoice")
|
17
|
+
invoice.items << InvoicePDF::LineItem.new(:description => "Here is a line item", :price => 495.00, :quantity => 5)
|
18
|
+
invoice.save
|
19
|
+
|
20
|
+
=== Using a generator
|
21
|
+
Generators are what actually create the PDF. This allows you to customize the appearance of your invoice by creating a new generator.
|
22
|
+
Have a look at lib/generators/standard.rb for an example.
|
23
|
+
|
24
|
+
Specifying the generator to use is easy. When creating your Invoice object, use the <tt>:generator</tt> attribute.
|
25
|
+
invoice = InvoicePDF::Invoice.new( :generator => InvoicePDF::Generators::Standard.new, :company => "Drew Tempelmeyer", :company_address => "555 55th St\nNew York, NY 00000", :bill_to => "John Doe", :number => "AZ-100", :notes => "Test invoice")
|
26
|
+
|
27
|
+
We're using the InvoicePDF::Generators::Standard generator to create our invoice. When you save the invoice, it will call the <tt>create_pdf</tt> method inside your generator
|
28
|
+
(in this example InvoicePDF::Generators::Standard.create_pdf).
|
29
|
+
|
30
|
+
=== Creating a generator
|
31
|
+
All generators should be contained within the InvoicePDF::Generators module. Your generator needs to have the <tt>create_pdf</tt> method.
|
32
|
+
|
33
|
+
A barebones generator would look something like the following
|
34
|
+
module InvoicePDF
|
35
|
+
module Generators
|
36
|
+
class Barebone
|
37
|
+
include InvoicePDF::Helpers # if you need the helpers
|
38
|
+
def create_pdf( invoice )
|
39
|
+
Prawn::Document.generate("#{invoice.file_path}/#{invoice.file_name}", :dpi => 72) do |pdf|
|
40
|
+
pdf.text "Invoice #{invoice.number}"
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
This would simply create a PDF in your specified directory with the specified file name.
|
48
|
+
|
49
|
+
PDF generation is done by using the Prawn gem (http://prawn.majesticseacreature.com/docs/).
|
50
|
+
|
51
|
+
== Contributing to InvoicePDF
|
52
|
+
|
53
|
+
* Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet
|
54
|
+
* Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it
|
55
|
+
* Fork the project
|
56
|
+
* Start a feature/bugfix branch
|
57
|
+
* Commit and push until you are happy with your contribution
|
58
|
+
* Make sure to add tests for it. This is important so I don't break it in a future version unintentionally.
|
59
|
+
* Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it.
|
60
|
+
|
61
|
+
== Copyright
|
62
|
+
|
63
|
+
Copyright (c) 2010 Drew Tempelmeyer. See LICENSE.txt for further details.
|
data/Rakefile
ADDED
@@ -0,0 +1,53 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'bundler'
|
3
|
+
begin
|
4
|
+
Bundler.setup(:default, :development)
|
5
|
+
rescue Bundler::BundlerError => e
|
6
|
+
$stderr.puts e.message
|
7
|
+
$stderr.puts "Run `bundle install` to install missing gems"
|
8
|
+
exit e.status_code
|
9
|
+
end
|
10
|
+
require 'rake'
|
11
|
+
|
12
|
+
require 'jeweler'
|
13
|
+
Jeweler::Tasks.new do |gem|
|
14
|
+
# gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
|
15
|
+
gem.name = "invoicepdf"
|
16
|
+
gem.homepage = "http://github.com/drewtempelmeyer/invoicepdf"
|
17
|
+
gem.license = "MIT"
|
18
|
+
gem.summary = %Q{Easily create PDF invoices}
|
19
|
+
gem.description = %Q{Easily create PDF invoices}
|
20
|
+
gem.email = "drewtemp@gmail.com"
|
21
|
+
gem.authors = ["Drew Tempelmeyer"]
|
22
|
+
gem.add_runtime_dependency 'prawn'
|
23
|
+
gem.files = FileList['Rakefile', 'lib/**/*.rb', 'init.rb']
|
24
|
+
gem.rubyforge_project = 'invoicepdf'
|
25
|
+
end
|
26
|
+
Jeweler::RubygemsDotOrgTasks.new
|
27
|
+
|
28
|
+
require 'rake/testtask'
|
29
|
+
Rake::TestTask.new(:test) do |test|
|
30
|
+
test.libs << 'lib' << 'test'
|
31
|
+
test.pattern = 'test/**/test_*.rb'
|
32
|
+
test.verbose = true
|
33
|
+
end
|
34
|
+
|
35
|
+
require 'rcov/rcovtask'
|
36
|
+
Rcov::RcovTask.new do |test|
|
37
|
+
test.libs << 'test'
|
38
|
+
test.pattern = 'test/**/test_*.rb'
|
39
|
+
test.verbose = true
|
40
|
+
end
|
41
|
+
|
42
|
+
task :default => :test
|
43
|
+
|
44
|
+
require 'rake/rdoctask'
|
45
|
+
Rake::RDocTask.new do |rdoc|
|
46
|
+
version = File.exist?('VERSION') ? File.read('VERSION') : ""
|
47
|
+
|
48
|
+
rdoc.rdoc_dir = 'rdoc'
|
49
|
+
rdoc.title = "invoicepdf #{version}"
|
50
|
+
rdoc.options << '-f' << 'horo'
|
51
|
+
rdoc.rdoc_files.include('README*')
|
52
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
53
|
+
end
|
data/init.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require 'invoicepdf'
|
@@ -0,0 +1,127 @@
|
|
1
|
+
module InvoicePDF
|
2
|
+
|
3
|
+
# Generators should be contained within the <tt>InvoicePDF::Generators</tt> module.
|
4
|
+
module Generators
|
5
|
+
|
6
|
+
# The default InvoicePDF generator.
|
7
|
+
class Standard
|
8
|
+
include InvoicePDF::Helpers
|
9
|
+
|
10
|
+
# Constructor here for future use... maybe.
|
11
|
+
def initialize( options = {} ); end
|
12
|
+
|
13
|
+
# Called from <tt>InvoicePDF::Invoice.save</tt>. +invoice+ is the <tt>InvoicePDF::Invoice</tt> instance.
|
14
|
+
def create_pdf( invoice )
|
15
|
+
money_maker_options = {
|
16
|
+
:currency_symbol => invoice.currency,
|
17
|
+
:delimiter => invoice.separator,
|
18
|
+
:decimal_symbol => invoice.decimal,
|
19
|
+
:after_text => invoice.currency_text
|
20
|
+
}
|
21
|
+
|
22
|
+
# The below line should remain consistent across all generators. We don't want mysteriously placed PDF files.
|
23
|
+
Prawn::Document.generate("#{invoice.file_path}/#{invoice.file_name}", :dpi => 72) do |pdf|
|
24
|
+
pdf.move_down 10
|
25
|
+
|
26
|
+
# Set the default type
|
27
|
+
pdf.font 'Helvetica', :size => 9
|
28
|
+
|
29
|
+
# Draw the company name
|
30
|
+
pdf.text invoice.company, :style => :bold, :size => 20
|
31
|
+
|
32
|
+
# Invoice information
|
33
|
+
pdf.bounding_box [ pdf.bounds.right - 200, pdf.bounds.top - 2 ], :width => 250 do
|
34
|
+
data = [
|
35
|
+
[ { :text => 'Invoice Number', :font_style => :bold }, invoice.number ],
|
36
|
+
[ { :text => 'Invoice Date', :font_style => :bold }, invoice.invoice_date ],
|
37
|
+
[ { :text => 'Due Date', :font_style => :bold }, invoice.due_date ]
|
38
|
+
]
|
39
|
+
|
40
|
+
data.insert( 1, [ { :text => 'PO Number', :font_style => :bold }, invoice.po_number ] ) unless invoice.po_number.nil?
|
41
|
+
|
42
|
+
pdf.table data,
|
43
|
+
:border_style => :underline_header,
|
44
|
+
:font_size => 9,
|
45
|
+
:horizontal_padding => 2,
|
46
|
+
:vertical_padding => 1,
|
47
|
+
:column_widths => { 0 => 100, 1 => 150 },
|
48
|
+
:position => :left,
|
49
|
+
:align => { 0 => :left, 1 => :left }
|
50
|
+
end
|
51
|
+
# End bounding_box
|
52
|
+
|
53
|
+
pdf.move_down 65
|
54
|
+
|
55
|
+
var_y = pdf.y
|
56
|
+
|
57
|
+
# Bill to section
|
58
|
+
pdf.bounding_box [ 0, var_y ], :width => ( pdf.bounds.right / 3 ) do
|
59
|
+
pdf.text 'Bill To', :style => :bold
|
60
|
+
pdf.text "#{invoice.bill_to}\n#{invoice.bill_to_address}"
|
61
|
+
end
|
62
|
+
# End bill to section
|
63
|
+
|
64
|
+
# Company address section
|
65
|
+
pdf.bounding_box [ ( pdf.bounds.right / 3 ), var_y ], :width => ( pdf.bounds.right / 3 ) do
|
66
|
+
pdf.text 'Pay To', :style => :bold
|
67
|
+
pdf.text "#{invoice.company}\n#{invoice.company_address}"
|
68
|
+
end
|
69
|
+
# End company address section
|
70
|
+
|
71
|
+
pdf.move_down 40
|
72
|
+
|
73
|
+
# Create items array for storage of invoice items
|
74
|
+
items = []
|
75
|
+
invoice.items.map { |item| items << [ item.description, item.quantity, money_maker(item.price, money_maker_options), money_maker(item.total, money_maker_options) ] }
|
76
|
+
|
77
|
+
# Insert subtotal
|
78
|
+
items << [ { :text => 'Subtotal', :align => :right, :colspan => 3 }, money_maker(invoice.subtotal, money_maker_options) ]
|
79
|
+
|
80
|
+
# Insert discount
|
81
|
+
items << [ { :text => "Discount (#{invoice.discount}%)", :align => :right, :colspan => 3 }, money_maker(invoice.discount_amount, money_maker_options) ] if invoice.discount_amount > 0
|
82
|
+
|
83
|
+
# Insert tax amount
|
84
|
+
items << [ { :text => "Tax (#{invoice.tax}%)", :align => :right, :colspan => 3 }, money_maker(invoice.tax_amount, money_maker_options) ] if invoice.tax_amount > 0
|
85
|
+
|
86
|
+
# Insert total
|
87
|
+
items << [ { :text => "Total", :align => :right, :colspan => 3 }, money_maker(invoice.total, money_maker_options) ]
|
88
|
+
|
89
|
+
# Insert amount paid
|
90
|
+
items << [ { :text => "Amount Paid", :align => :right, :colspan => 3 }, money_maker(invoice.paid, money_maker_options) ] if invoice.paid > 0
|
91
|
+
|
92
|
+
# Insert total due
|
93
|
+
items << [ { :text => "Amount Due", :align => :right, :colspan => 3, :font_style => :bold }, money_maker(invoice.total_due, money_maker_options) ]
|
94
|
+
|
95
|
+
# Create items table
|
96
|
+
pdf.table items,
|
97
|
+
:border_style => :underline_header,
|
98
|
+
:border_width => 0,
|
99
|
+
:border_color => '000000',
|
100
|
+
:font_size => 9,
|
101
|
+
:headers => [
|
102
|
+
{ :text => 'Description', :font_style => :bold },
|
103
|
+
{ :text => 'Qty', :font_style => :bold },
|
104
|
+
{ :text => 'Price', :font_style => :bold },
|
105
|
+
{ :text => 'Total', :font_style => :bold }
|
106
|
+
],
|
107
|
+
:header_color => '000000',
|
108
|
+
:header_text_color => 'ffffff',
|
109
|
+
:align => { 0 => :left, 1 => :center, 2 => :center, 3 => :right },
|
110
|
+
:row_colors => [ 'ffffff', 'f0f0f0' ],
|
111
|
+
:width => pdf.bounds.right,
|
112
|
+
:column_widths => { 1 => 50, 2 => 75, 3 => 75 }
|
113
|
+
|
114
|
+
unless invoice.notes.nil?
|
115
|
+
pdf.move_down 50
|
116
|
+
pdf.text 'Notes', :size => 10, :style => :bold
|
117
|
+
pdf.text invoice.notes, :size => 8
|
118
|
+
end
|
119
|
+
end
|
120
|
+
# Done creating PDF
|
121
|
+
|
122
|
+
end
|
123
|
+
|
124
|
+
end
|
125
|
+
|
126
|
+
end
|
127
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module InvoicePDF
|
2
|
+
# Helpers to be used in InvoicePDF::Generators
|
3
|
+
module Helpers
|
4
|
+
|
5
|
+
# Similar to Rails' number_to_currency, but we don't want to rely on Rails.
|
6
|
+
# Found the majority of this code online, but lost the URL. If this is yours, let me know and I'll give
|
7
|
+
# credit.
|
8
|
+
#
|
9
|
+
# Formats the +number+ into a currency format. You can customize the format in the +options+ hash.
|
10
|
+
#
|
11
|
+
# ==== Options
|
12
|
+
# * <tt>:currency_symbol</tt> - Currency symbol. (default is '$')
|
13
|
+
# * <tt>:delimiter</tt> - Thousands separator. (default is ',')
|
14
|
+
# * <tt>:decimal_symbol</tt> - Separates integer and fractional amounts (think dollars and cents). (default is '.')
|
15
|
+
# * <tt>:currency_before</tt> - Boolean to set placement of <tt>:currency_symbol</tt>. true for before number, false for after number. (default is true)
|
16
|
+
# * <tt>:after_text</tt> - Useful for displaying the currency after the number. (default is ' USD')
|
17
|
+
#
|
18
|
+
# ==== Example
|
19
|
+
# money_maker(1000) # => $1,000.00 USD
|
20
|
+
# money_maker(1000, :delimiter => '.', :decimal_symbol => ',', :after_text => '') # => $1.000,00
|
21
|
+
def money_maker( number, options = {} )
|
22
|
+
options = { :currency_symbol => '$', :delimiter => ',', :decimal_symbol => '.', :currency_before => true, :after_text => ' USD' }.merge(options)
|
23
|
+
|
24
|
+
# split integer and fractional parts
|
25
|
+
int, frac = ("%.2f" % number).split('.')
|
26
|
+
|
27
|
+
# insert delimiters
|
28
|
+
int.gsub!(/(\d)(?=(\d\d\d)+(?!\d))/, "\\1#{options[:delimiter]}")
|
29
|
+
|
30
|
+
return options[:currency_symbol] + int + options[:decimal_symbol] + frac + options[:after_text] if options[:currency_before]
|
31
|
+
int + options[:decimal_symbol] + frac + options[:currency_symbol] + options[:after_text]
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,82 @@
|
|
1
|
+
|
2
|
+
# One module to rule them all
|
3
|
+
module InvoicePDF
|
4
|
+
|
5
|
+
# Invoice model to create your PDF invoices.
|
6
|
+
class Invoice
|
7
|
+
|
8
|
+
attr_accessor :generator, :file_name, :file_path, :company, :company_address, :bill_to, :bill_to_address, :invoice_date, :due_date,
|
9
|
+
:number, :po_number, :items, :tax, :discount, :paid, :notes, :currency, :separator, :decimal, :currency_text
|
10
|
+
|
11
|
+
# Creates a new Invoice object with the specified options
|
12
|
+
# ==== Options
|
13
|
+
# * <tt>:generator</tt> - An <tt>InvoicePDF::Generator</tt> that designs and creates the PDF. (default is InvoicePDF::Generators::Standard.new)
|
14
|
+
# * <tt>:file_path</tt> - The directory to store the generated invoice. (default is './')
|
15
|
+
# * <tt>:file_name</tt> - The file name of the PDF to save. (default is 'invoice.pdf')
|
16
|
+
# * <tt>:company</tt> - The company name that will appear on the top of the invoice and in the 'Bill To' section. (default is 'Company Name')
|
17
|
+
# * <tt>:company_address</tt> - Your company's address. Displayed in the 'Pay To' section. Use \n for line separation. (default is nil)
|
18
|
+
# * <tt>:bill_to</tt> - Person/company you're billing. (default is nil)
|
19
|
+
# * <tt>:bill_to_address</tt> - Address of the person/company you're billing to. Displayed in the 'Bill To' section. (default is nil)
|
20
|
+
# * <tt>:number</tt> - The invoice number. (default is '100')
|
21
|
+
# * <tt>:po_number</tt> - The purchase order number for the invoice. Excluded from the invoice if nil. (default is nil)
|
22
|
+
# * <tt>:invoice_date</tt> - The invoice date. (default is Time.now)
|
23
|
+
# * <tt>:due_date</tt> - The invoice due_date. You need your money by this time. (default is 15 days after Time.now)
|
24
|
+
# * <tt>:items</tt> - Array of <tt>LineItem</tt>s. (default is an empty array).
|
25
|
+
# * <tt>:discount</tt> - Discount to apply to the invoice. The value should not include the percent symbol. (default is nil)
|
26
|
+
# * <tt>:tax</tt> - Tax amount to apply to taxable <tt>LineItem</tt>s. The value should not include the percent symbol. (default is nil)
|
27
|
+
# * <tt>:paid</tt> - The amount already paid on the invoice. (default is 0)
|
28
|
+
# * <tt>:notes</tt> - Notes to display at the end of the invoice. (default is nil)
|
29
|
+
# * <tt>:currency</tt> - The currency symbol to use for formatting prices and totals. (default is '$')
|
30
|
+
# * <tt>:separator</tt> - The thousands separator for prices/totals. (default is ',')
|
31
|
+
# * <tt>:decimal</tt> - The separator between the integer and fractional value. Think dollars and cents. (default is '.')
|
32
|
+
# * <tt>:currency_text</tt> - Displays text after the formatted currency. Useful if you wanted to include the currency code (example: ' USD'). (default is '')
|
33
|
+
#
|
34
|
+
# ==== Examples
|
35
|
+
# invoice = InvoicePDF::Invoice.new( :company => "Drew Tempelmeyer", :company_address => "555 55th St\nNew York, NY 00000", :bill_to => "John Doe", :number => "AZ-100", :notes => "Test invoice")
|
36
|
+
# invoice.items << InvoicePDF::LineItem.new(:description => "Here is a line item", :price => 495.00, :quantity => 5)
|
37
|
+
# invoice.save
|
38
|
+
def initialize( options = {} )
|
39
|
+
options = { :generator => InvoicePDF::Generators::Standard.new, :file_path => './', :file_name => 'invoice.pdf', :company => 'Company Name', :number => '100',
|
40
|
+
:po_number => nil, :invoice_date => Time.now, :due_date => Time.now + ((60 * 60 * 24) * 15), :items => [], :discount => nil, :tax => nil, :paid => 0,
|
41
|
+
:notes => nil, :currency => '$', :separator => ',', :decimal => '.', :currency_text => '' }.merge(options)
|
42
|
+
options.each { |k,v| send("#{k}=", v) }
|
43
|
+
end
|
44
|
+
|
45
|
+
# Calculates the subtotal of the invoice
|
46
|
+
def subtotal
|
47
|
+
amount = 0
|
48
|
+
items.each { |item| amount += item.total }
|
49
|
+
amount
|
50
|
+
end
|
51
|
+
|
52
|
+
# Calculates the tax amount of the invoice based on taxable items
|
53
|
+
def tax_amount
|
54
|
+
return 0 if tax.nil? || tax <= 0
|
55
|
+
amount = 0
|
56
|
+
items.each { |item| amount += item.total if item.taxable? }
|
57
|
+
amount * ( tax / 100 )
|
58
|
+
end
|
59
|
+
|
60
|
+
# Calculates the discount amount if a discount is specified greater than 0
|
61
|
+
def discount_amount
|
62
|
+
return 0 if discount.nil? || discount <= 0
|
63
|
+
subtotal * ( discount / 100 )
|
64
|
+
end
|
65
|
+
|
66
|
+
# Calculates the total of the invoice with the discount and tax amount applied
|
67
|
+
def total
|
68
|
+
(subtotal - discount_amount) + tax_amount
|
69
|
+
end
|
70
|
+
|
71
|
+
# The total amount due (total - paid)
|
72
|
+
def total_due
|
73
|
+
total - paid
|
74
|
+
end
|
75
|
+
|
76
|
+
# Generates the PDF invoice and saves it to the specified destination
|
77
|
+
def save
|
78
|
+
generator.create_pdf( self )
|
79
|
+
end
|
80
|
+
|
81
|
+
end
|
82
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
module InvoicePDF #:nodoc#
|
2
|
+
# Line items for InvoicePDF::Invoice
|
3
|
+
class LineItem
|
4
|
+
attr_accessor :sku, :description, :price, :quantity, :taxable
|
5
|
+
|
6
|
+
# Creates a new <tt>LineItem</tt> to be added to the InvoicePDF::Invoice instance
|
7
|
+
#
|
8
|
+
# ==== Options
|
9
|
+
# * <tt>:sku</tt> - SKU of the item. (default is nil)
|
10
|
+
# * <tt>:description</tt> - Description of the item. (default is nil)
|
11
|
+
# * <tt>:price</tt> - Price of each item. (default is 0.00)
|
12
|
+
# * <tt>:quantity</tt> - Number of items. (default is 1)
|
13
|
+
# * <tt>:taxable</tt> - Is the item taxable? true/false. (default is false)
|
14
|
+
#
|
15
|
+
# ==== Example
|
16
|
+
# item = InvoicePDF::InvoiceItem.new(:description => "Test line item", :price => 495.00, :quantity => 5)
|
17
|
+
def initialize(options = {})
|
18
|
+
options = { :sku => nil, :description => nil, :quantity => 1, :price => 0.00, :taxable => false }.merge(options)
|
19
|
+
options.each { |k, v| send("#{k}=", v) }
|
20
|
+
end
|
21
|
+
|
22
|
+
# Asks if the item is taxable
|
23
|
+
# item = InvoicePDF::InvoiceItem.new(:description => "Test line item", :price => 495.00, :quantity => 5)
|
24
|
+
# item.taxable? # => false
|
25
|
+
def taxable?
|
26
|
+
return false if taxable.nil? || taxable == false
|
27
|
+
true
|
28
|
+
end
|
29
|
+
|
30
|
+
# Return the total amount of the <tt>LineItem</tt>
|
31
|
+
# item = InvoicePDF::InvoiceItem.new(:description => "Test line item", :price => 495.00, :quantity => 5)
|
32
|
+
# item.total # => 2475
|
33
|
+
def total
|
34
|
+
price * quantity
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
38
|
+
end
|
data/lib/invoicepdf.rb
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
module InvoicePDF
|
2
|
+
# InvoicePDF version
|
3
|
+
VERSION = "0.1.2"
|
4
|
+
end
|
5
|
+
|
6
|
+
require 'prawn'
|
7
|
+
require 'prawn/layout'
|
8
|
+
require 'invoice/invoice'
|
9
|
+
require 'invoice/line_item'
|
10
|
+
require 'invoice/helpers'
|
11
|
+
|
12
|
+
Dir[File.dirname(__FILE__) + '/generators/*.rb'].each { |file| require file }
|
data/test/helper.rb
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'bundler'
|
3
|
+
begin
|
4
|
+
Bundler.setup(:default, :development)
|
5
|
+
rescue Bundler::BundlerError => e
|
6
|
+
$stderr.puts e.message
|
7
|
+
$stderr.puts "Run `bundle install` to install missing gems"
|
8
|
+
exit e.status_code
|
9
|
+
end
|
10
|
+
require 'test/unit'
|
11
|
+
require 'shoulda'
|
12
|
+
|
13
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
14
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
15
|
+
require 'invoice'
|
16
|
+
|
17
|
+
class Test::Unit::TestCase
|
18
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'helper'
|
2
|
+
|
3
|
+
class TestInvoice < Test::Unit::TestCase
|
4
|
+
|
5
|
+
should "start a new invoice" do
|
6
|
+
invoice = InvoicePDF::Invoice.new(:company => 'Drew Tempelmeyer', :bill_to => 'Alyssa Smith', :notes => 'Pay up now, sucka')
|
7
|
+
assert_equal 'Drew Tempelmeyer', invoice.company
|
8
|
+
end
|
9
|
+
|
10
|
+
should "insert a new item into the invoice" do
|
11
|
+
invoice = InvoicePDF::Invoice.new({ :company => 'Drew Tempelmeyer', :bill_to => 'Alyssa Smith', :notes => 'Pay up now, sucka' })
|
12
|
+
invoice.items << InvoicePDF::LineItem.new({ :description => 'This is a line item', :price => 400, :quantity => 100 })
|
13
|
+
assert_equal 1, invoice.items.length
|
14
|
+
end
|
15
|
+
|
16
|
+
should "subtotal should equal 40000" do
|
17
|
+
invoice = InvoicePDF::Invoice.new({ :company => 'Drew Tempelmeyer', :bill_to => 'Alyssa Smith', :notes => 'Pay up now, sucka' })
|
18
|
+
invoice.items << InvoicePDF::LineItem.new({ :description => 'This is a line item', :price => 400, :quantity => 100 })
|
19
|
+
assert_equal 40000, invoice.subtotal
|
20
|
+
end
|
21
|
+
|
22
|
+
should "have paid half of the invoice" do
|
23
|
+
invoice = InvoicePDF::Invoice.new({ :company => 'Drew Tempelmeyer', :bill_to => 'Alyssa Smith', :notes => 'Pay up now, sucka' })
|
24
|
+
invoice.items << InvoicePDF::LineItem.new({ :description => 'This is a line item', :price => 400, :quantity => 100 })
|
25
|
+
invoice.paid = 20000
|
26
|
+
assert_equal 20000, invoice.total_due
|
27
|
+
end
|
28
|
+
|
29
|
+
should "save the PDF" do
|
30
|
+
invoice = InvoicePDF::Invoice.new({ :company => 'Drew Tempelmeyer', :bill_to => 'Alyssa Smith', :notes => 'Pay up now, sucka' })
|
31
|
+
invoice.items << InvoicePDF::LineItem.new({ :description => 'This is a line item', :price => 400, :quantity => 100 })
|
32
|
+
invoice.paid = 20000
|
33
|
+
invoice.save
|
34
|
+
assert_equal true, File.exist?('invoice.pdf')
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
metadata
ADDED
@@ -0,0 +1,171 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: invoicepdf
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease: false
|
5
|
+
segments:
|
6
|
+
- 0
|
7
|
+
- 1
|
8
|
+
- 4
|
9
|
+
version: 0.1.4
|
10
|
+
platform: ruby
|
11
|
+
authors:
|
12
|
+
- Drew Tempelmeyer
|
13
|
+
autorequire:
|
14
|
+
bindir: bin
|
15
|
+
cert_chain: []
|
16
|
+
|
17
|
+
date: 2010-11-21 00:00:00 -06:00
|
18
|
+
default_executable:
|
19
|
+
dependencies:
|
20
|
+
- !ruby/object:Gem::Dependency
|
21
|
+
name: prawn
|
22
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
23
|
+
none: false
|
24
|
+
requirements:
|
25
|
+
- - ">="
|
26
|
+
- !ruby/object:Gem::Version
|
27
|
+
segments:
|
28
|
+
- 0
|
29
|
+
version: "0"
|
30
|
+
type: :runtime
|
31
|
+
prerelease: false
|
32
|
+
version_requirements: *id001
|
33
|
+
- !ruby/object:Gem::Dependency
|
34
|
+
name: shoulda
|
35
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
36
|
+
none: false
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
segments:
|
41
|
+
- 0
|
42
|
+
version: "0"
|
43
|
+
type: :development
|
44
|
+
prerelease: false
|
45
|
+
version_requirements: *id002
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: bundler
|
48
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
49
|
+
none: false
|
50
|
+
requirements:
|
51
|
+
- - ~>
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
segments:
|
54
|
+
- 1
|
55
|
+
- 0
|
56
|
+
- 0
|
57
|
+
version: 1.0.0
|
58
|
+
type: :development
|
59
|
+
prerelease: false
|
60
|
+
version_requirements: *id003
|
61
|
+
- !ruby/object:Gem::Dependency
|
62
|
+
name: jeweler
|
63
|
+
requirement: &id004 !ruby/object:Gem::Requirement
|
64
|
+
none: false
|
65
|
+
requirements:
|
66
|
+
- - ~>
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
segments:
|
69
|
+
- 1
|
70
|
+
- 5
|
71
|
+
- 1
|
72
|
+
version: 1.5.1
|
73
|
+
type: :development
|
74
|
+
prerelease: false
|
75
|
+
version_requirements: *id004
|
76
|
+
- !ruby/object:Gem::Dependency
|
77
|
+
name: rcov
|
78
|
+
requirement: &id005 !ruby/object:Gem::Requirement
|
79
|
+
none: false
|
80
|
+
requirements:
|
81
|
+
- - ">="
|
82
|
+
- !ruby/object:Gem::Version
|
83
|
+
segments:
|
84
|
+
- 0
|
85
|
+
version: "0"
|
86
|
+
type: :development
|
87
|
+
prerelease: false
|
88
|
+
version_requirements: *id005
|
89
|
+
- !ruby/object:Gem::Dependency
|
90
|
+
name: horo
|
91
|
+
requirement: &id006 !ruby/object:Gem::Requirement
|
92
|
+
none: false
|
93
|
+
requirements:
|
94
|
+
- - ">="
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
segments:
|
97
|
+
- 0
|
98
|
+
version: "0"
|
99
|
+
type: :development
|
100
|
+
prerelease: false
|
101
|
+
version_requirements: *id006
|
102
|
+
- !ruby/object:Gem::Dependency
|
103
|
+
name: prawn
|
104
|
+
requirement: &id007 !ruby/object:Gem::Requirement
|
105
|
+
none: false
|
106
|
+
requirements:
|
107
|
+
- - ">="
|
108
|
+
- !ruby/object:Gem::Version
|
109
|
+
segments:
|
110
|
+
- 0
|
111
|
+
version: "0"
|
112
|
+
type: :runtime
|
113
|
+
prerelease: false
|
114
|
+
version_requirements: *id007
|
115
|
+
description: Easily create PDF invoices
|
116
|
+
email: drewtemp@gmail.com
|
117
|
+
executables: []
|
118
|
+
|
119
|
+
extensions: []
|
120
|
+
|
121
|
+
extra_rdoc_files:
|
122
|
+
- LICENSE.txt
|
123
|
+
- README.rdoc
|
124
|
+
files:
|
125
|
+
- Rakefile
|
126
|
+
- init.rb
|
127
|
+
- lib/generators/standard.rb
|
128
|
+
- lib/invoice/helpers.rb
|
129
|
+
- lib/invoice/invoice.rb
|
130
|
+
- lib/invoice/line_item.rb
|
131
|
+
- lib/invoicepdf.rb
|
132
|
+
- LICENSE.txt
|
133
|
+
- README.rdoc
|
134
|
+
- test/helper.rb
|
135
|
+
- test/test_invoice.rb
|
136
|
+
has_rdoc: true
|
137
|
+
homepage: http://github.com/drewtempelmeyer/invoicepdf
|
138
|
+
licenses:
|
139
|
+
- MIT
|
140
|
+
post_install_message:
|
141
|
+
rdoc_options: []
|
142
|
+
|
143
|
+
require_paths:
|
144
|
+
- lib
|
145
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
146
|
+
none: false
|
147
|
+
requirements:
|
148
|
+
- - ">="
|
149
|
+
- !ruby/object:Gem::Version
|
150
|
+
hash: 419636474764141488
|
151
|
+
segments:
|
152
|
+
- 0
|
153
|
+
version: "0"
|
154
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
155
|
+
none: false
|
156
|
+
requirements:
|
157
|
+
- - ">="
|
158
|
+
- !ruby/object:Gem::Version
|
159
|
+
segments:
|
160
|
+
- 0
|
161
|
+
version: "0"
|
162
|
+
requirements: []
|
163
|
+
|
164
|
+
rubyforge_project: invoicepdf
|
165
|
+
rubygems_version: 1.3.7
|
166
|
+
signing_key:
|
167
|
+
specification_version: 3
|
168
|
+
summary: Easily create PDF invoices
|
169
|
+
test_files:
|
170
|
+
- test/helper.rb
|
171
|
+
- test/test_invoice.rb
|